メインコンテンツにスキップ
モジュール 10 · レッスン 5 / 5コース内の 50/50~18 min
モジュールのレッスン (5/5)

ミニプロジェクト: HTTP サーバー

net/http stdlib パッケージは、ルーティング、ハンドラー、ミドルウェア、正常なシャットダウンなど、本番環境に対応した HTTP サーバーを構築するのに十分です。フレームワークは必要ありません。一般的な REST API の場合、これが基礎となります。

こんにちは世界

Go
package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "hello %s", r.URL.Path[1:])
    })
    if err := http.ListenAndServe(":8080", nil); err != nil {
        panic(err)
    }
}

http.ListenAndServe はブロックし、エラーの場合のみ返します。 2 番目の引数として nil = 「DefaultServeMux を使用する」。

ハンドラーの署名

すべてのハンドラーは同じ署名を共有します。

Go
func(w http.ResponseWriter, r *http.Request)
  • w http.ResponseWriter — 出力: ヘッダー、ステータス コード、本文。
  • r *http.Request — 入力: メソッド、URL、ヘッダー、本文、コンテキスト。

典型的なパターン:

Go
func ping(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
        return
    }
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK) // optional, defaults to 200
    fmt.Fprintln(w, `{"ok":true}`)
}

ServeMux によるルーティング

*http.ServeMux はベースルーターです。 Go 1.22 以降、メソッドとパスのパラメーターを含むパターンがサポートされています。

Go
mux := http.NewServeMux()
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    fmt.Fprintln(w, "user", id)
})
mux.HandleFunc("POST /users", createUser)

http.ListenAndServe(":8080", mux)

より高度なルーター (グループ、統合ミドルウェア、正規表現) には、chi、gorilla/mux、gin、echo があります。 stdlib はケースの 90% をカバーします。

デコレータとしてのミドルウェア

ミドルウェアは関数 func(http.Handler) http.Handler です。

Go
func logMW(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start))
    })
}

http.ListenAndServe(":8080", logMW(mux))

それらをチェーンするには: logMW(authMW(rateLimitMW(mux)))。各ミドルウェアが次のミドルウェアを飾ります。

http.Server による細かい制御

http.ListenAndServe(addr, h) はショートカットです。タイムアウト、TLS、および正常なシャットダウンの場合は、http.Server をインスタンス化する必要があります。

Go
srv := &http.Server{
    Addr:              ":8080",
    Handler:           mux,
    ReadHeaderTimeout: 5 * time.Second,
    ReadTimeout:       30 * time.Second,
    WriteTimeout:      30 * time.Second,
    IdleTimeout:       2 * time.Minute,
}
log.Fatal(srv.ListenAndServe())

コンテキストを使用した正常なシャットダウン

プロセスが SIGINT/SIGTERM を受信したら、終了する前に実行中のリクエストを終了したいとします。

Go
ctx, stop := signal.NotifyContext(context.Background(),
    os.Interrupt, syscall.SIGTERM)
defer stop()

srv := &http.Server{Addr: ":8080", Handler: mux}
go func() {
    if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
        log.Fatal(err)
    }
}()

<-ctx.Done() // wait for the signal

shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
    log.Println("shutdown:", err)
}

srv.Shutdown は、新しい接続の受け入れを停止し、アクティブな接続が終了するまで (渡された期限まで) 待機します。

JSON本体の読み取り

Go
type CreateUserReq struct {
    Name string `json:"name"`
}

func createUser(w http.ResponseWriter, r *http.Request) {
    var req CreateUserReq
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    // ... use req.Name
    w.WriteHeader(http.StatusCreated)
}

r.Body は io.ReadCloser です。サーバーは自動的に閉じますが、json.NewDecoder は最初の JSON オブジェクトを超えて読み取りません (入力の検証に役立ちます)。

演習

運動#go.m10.l5.e1
試行回数: 0読み込み中…

レジストラは、ハンドラー こんにちは、ルート '/' を使用して http.HandleFunc を実行します。

エディターを読み込み中…

3 回の試行後に解決策が利用可能になります

運動#go.m10.l5.e2
試行回数: 0読み込み中…

Avvia il サーバー su :8080 con http.ListenAndServe e stampal'errore se non è nil。

エディターを読み込み中…

3 回の試行後に解決策が利用可能になります

Quiz#go.m10.l5.e3
準備完了

Go の HTTP ハンドラーは適​​切ですか?

Go
func H(???) { ... }
回答の選択肢