GoのHTTPサーバーのいろいろなパターンについて紹介します。
コード:https://github.com/tkeshun/http-server-explain
基本的なHTTPサーバー
パスに対応した処理が行われるように関数を登録します。
以下例では"http://<ホスト名>:8080/"にアクセスしたとき紐付けられた関数が実行されます。
関数はHandleFuncの引数に定義されたfunc(ResponseWriter, *Request)
型にマッチすれば引数として渡すことができます。
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func main() {
http.HandleFunc("/", handler) // DefaultServeMuxにパスと対応する処理が登録される
http.ListenAndServe(":8080", nil)
}
ライブラリのHTTPリクエストマルチプレクサを使ってHTTPサーバーを組み立てる方法
前項のコードの場合、何も指定していないため、DefaultServMux
というHTTPリクエストマルチプレクサが使用されている。
HTTPリクエストマルチプレクサは自分で変数宣言して使うことが可能である。
http.NewServeMux()
で新たなマルチプレクサを作れる。
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", handler)
// マルチプレクサ(handler)を登録する必要がある
// 登録する場合、第一引数のポート番号と第2引数のhandlerがServer構造体に登録された上で、ServerのListenAndServeメソッドが実行される
http.ListenAndServe(":8080", mux)
}
Handlerを自分で実装するパターン
今までのコードでは、予め内部で実装済みのServeHTTPメソッドが呼び出されていた。
http.Handlerインターフェースを実装した構造体を用意することで、中身の処理を自身で記述できるようになる
以下の例では、 フィールドなしの構造体を定義して、ServeHTTPメソッドを実装している。
http.Handle()はhttp.Handlerインターフェースを満たす構造体を要求するので、ServeHTTPを実装し、http.Handlerインターフェースを満たすことで引数に渡すことができるようになる。
カスタムハンドラーはこのあと紹介するミドルウェアパターンで役に立つ。
package main
import "net/http"
type CustomHandler struct{} // anyだとエラーになる
// http.Handlerのインターフェースを実装する
func (h *CustomHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Custom handler\n"))
}
func main() {
handler := &CustomHandler{}
// DefaultServeMuxにカスタムハンドラーを登録する
http.Handle("/", handler)
http.ListenAndServe(":8080", nil)
}
ミドルウェアパターン
http.Handlerをラップすることで、ラップされるハンドラーの前後に処理を追加することができる 入れ子に組み合わせることで複数のミドルウェアを組み合わせられる。
これをミドルウェアパターンという。
ここでは、middlewareでhttp.Handlerをラップしている。
関数の中でnext.ServeHTTP(w, r)
を呼び出すことで、本来実行されるはずだったServeHTTP関数を実行しつつ、引数のハンドラーが実行される前後に処理を挿むことができる。
以下が処理の流れの図。
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("ラップする処理の前に実行される")
next.ServeHTTP(w, r)
fmt.Println("ラップする処理の後に実行される")
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", handler)
mux.Handle("/middleware", middleware(http.HandlerFunc(handler)))
// マルチプレクサ(handler)を登録する必要がある
// 登録する場合、第一引数のポート番号と第2引数のhandlerがServer構造体に登録された上で、ServerのListenAndServeメソッドが実行される
http.ListenAndServe(":8080", mux)
}
まとめ
GoのWebサーバーの使い方の基本を紹介した。
リポジトリにある他の使い方もそのうち紹介する。