HTTPの特徴
ステートレス
HTTPは「ステートレス(状態を持たない)」なプロトコルです。一度の通信が終了すると、サーバーはその通信の状態を保持しません。
例:AさんがページAを開いた後にページBを開いても、サーバーはAさんが以前にページAを見たことを覚えていません。
この問題を解決するためにCookieやセッションが利用されます。
リクエストとレスポンスの組み合わせ
HTTP通信は、クライアントからのリクエストとサーバーからのレスポンスによって成り立ちます。
HTTPのリクエスト
リクエストの構造
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
<空行が入る>
<ボディ(必要に応じて)>
- メソッド:リクエストの種類を指定(例:GET, POST, PUT, DELETE)
-
リクエストターゲット:リソース(例:
/index.html
)の場所を指定 - HTTPバージョン:HTTPのバージョン(例:HTTP/1.1, HTTP/2)
-
ヘッダー情報:リクエストに関する追加情報(例:
User-Agent
でクライアントの種類を指定) - ボディ:データを送信する場合に使用(例:フォームのデータなど)
HTTPのレスポンス
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 138
<html>
<body>
<h1>Hello, World!</h1>
</body>
</html>
-
ステータスコード:リクエストの結果を表す番号(例:200, 404, 500)
-
200 OK
:成功 -
404 Not Found
:リソースが見つからない -
500 Internal Server Error
:サーバーエラー
-
-
ヘッダー情報:レスポンスに関する追加情報(例:
Content-Type
でデータ形式を指定) - ボディ:クライアントに返すデータ(例:HTML, JSON)
HTTPメソッド
HTTPメソッドは、クライアントがサーバーに対してどのような動作を要求するかを示します。
メソッド | 説明 | 主な用途 |
---|---|---|
GET | リソースを取得する | Webページの表示 |
POST | データを送信して処理を要求する | フォーム送信 |
PUT | リソースを更新する | データの更新 |
DELETE | リソースを削除する | データの削除 |
HEAD | リソースのヘッダーだけを取得 | 状態確認 |
HTTPとHTTPSの違い
HTTP
- データは暗号化されず、平文で送信される
- セキュリティに問題がある(盗聴や改ざんのリスク)
HTTPS
- データをSSL/TLSで暗号化して送信
- セキュリティが向上し、安全な通信を実現
HTTPSの仕組み
- サーバーがSSL証明書を使ってクライアントに自身を証明
- クライアントとサーバーが共通鍵を生成し、データを暗号化
HTTPの進化
HTTP/1.1
- 一度の接続で複数リクエストを送れる「keep alive」をサポート
HTTP/2
- データをバイナリ形式で送信し、高速化
- ヘッダー圧縮により通信量を削減
- 1つの接続で複数のデータを同時に送信
HTTP/3
- UDPベースの通信プロトコル「QUIC」を採用
- 接続速度の高速化と遅延の改善
Go言語でHTTPを扱う例
HTTPサーバーの基本的な実装
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
// クライアントからのリクエスト情報をログに出力
fmt.Printf("Method: %s, Path: %s\n", r.Method, r.URL.Path)
// レスポンスを送信
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Hello, HTTP Server!")
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Starting server on :8080")
http.ListenAndServe(":8080", nil)
}
サーバー側
Starting server on :8080
Method: GET, Path: /
HTTPクライアントの基本的な実装
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
// GETリクエストを送信
resp, err := http.Get("https://jsonplaceholder.typicode.com/posts/1")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
// ステータスコードを出力
fmt.Println("Status Code:", resp.StatusCode)
// レスポンスボディを読み取る
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading body:", err)
return
}
fmt.Println("Response Body:", string(body))
}
Status Code: 200
Response Body: {
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
JSONデータの送受信
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type RequestData struct {
Name string `json:"name"`
Age int `json:"age"`
}
func jsonHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
var data RequestData
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
response := fmt.Sprintf("Received: Name=%s, Age=%d", data.Name, data.Age)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"message": response})
}
func main() {
http.HandleFunc("/submit", jsonHandler)
fmt.Println("Server started at :8080")
http.ListenAndServe(":8080", nil)
}
postman等でPOSTリクエストを送信してリクエストを確認
{
"message": "Received: Name=taro, Age=20"
}