背景
- GoのHTTP通信に使う標準ライブラリnet/httpには、型名や関数名として似た単語がたくさん出てくる。Handler, HandleFunc, Handleなど。
- それらを整理しておきたい!
net/http.Server
- Goでサーバを立てるにはServer構造体を初期化しなければならない。
- サーバを立てるのに、Server構造体を初期化するというのは、わかりやすい。
$ go doc net/http.Server
type Server struct {
// Addr optionallu specifies the TCP address for the server to listen on,
// in the form "host:port". If empty, ":http" (port 80) is used.
Addr string
Handler Handler // handler to invoke, http.DefaultServeMux if nil
省略
}
func (srv *Server) Close() error
func (srv *Server) ListenAndServe() error
func (srv *Server) Serve(l net.Listener) error
省略
- Server構造体には色々なメンバがあるが基本は次のふたつ。
-
AddrにIPアドレスとポート番号を組み合わせた文字列(ex. 127.0.0.1:8000)を登録する。
-
HandlerにはHandler interfaceとして振る舞う型を登録する。
net/http.Handler
- Handlerは、ServeHTTPというメソッドを実装したinterfaceである。
- このServeHTTPが、HTTPリクエストからHTTPレスポンスを返す処理を実装する。
$ go doc net/http.Handler
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
A Handler responds to an HTTP request.
ServeHTTP should reply headers and data to the ResponseWriter and then return.
Returning signals that the request is finished; it is not valid to use the
ResponseWriter or read from the Request.Body after or concurrently with the
completion of the ServeHTTP call.
net/http.ServeMux
- ServeMuxを利用することで、リクエストURLのパターンに応じて異なるHandlerに処理を振り分けることができます。
- HandlerはそれぞれServeHTTPメソッドでリクエストを処理してレスポンスを返します。
- ServeMuxにURLのパターンとHandlerのセットを登録しておくことで、特定のURLパターンが来たら特定のHandlerの処理を振ることができます。
- ServerにServeMuxを設定するときは、ServerのHandlerメンバにServeMuxをセットします。ServeMuxはServeHTTPメソッドを実装しているのでHandler interfaceとして振る舞えるのです。
- ServeMuxが持っている役割は、URLパターンに応じて登録されたHandlerに処理を振り分けることだけです。
$ go doc net/http.ServeMux
type ServeMux struct {
// Has unexported fields.
}
ServeMux is an HTTP request multiplexer. It matches the URL of each incoming
request against a list of registered patterns and calls the handler for the
pattern that most closely matches the URL.
省略
func NewServeMux() *ServeMux
func (mux *ServeMux) Handle(pattern string, handler Handler)
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string)
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)
net/http.ServeMux.Handle
- ServeMuxのHandleメソッドを使うことで、ServeMuxに特定のURLパターンに対応するHandlerを登録できます。
- 引数handlerは、このメソッドのシグネチャより、Handler interfaceとして振る舞える必要があります。つまり、ServeHTTPメソッドを実装している型でなければいけません。
$ go doc net/http.ServeMux.Handle
func (mux *ServeMux) Handle(pattern string, handler Handler)
Handle registers the handler for the given pattern. If a handler already
exists for pattern, Handle panics.
net/http.ServeMux.HandleFunc
- ServeMuxのHandleFuncメソッドは、Handleと同じ機能を果たしますが、シグネチャが少し異なります。
- さきほどのHandleは2番目の引数でHandlerをとったのに対して、HandleFuncではfunc(ResponseWriter, *Request)をとります。
- つまり、HandleFuncで指定するにはHandlerでなくてもよいのです。ServeHTTPメソッドを実装している型でなくてもよいということです。
$ go doc net/http.ServeMux.HandleFunc
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
HandleFunc registers the handler function for the given pattern.
- HandleFuncの具体的な実装が以下になります。
- 引数として受け取ったfunc(ResponseWriter, *Request)を、HandlerFunc()に通して、先ほどのHandleでServeMuxにHandlerとして登録しています。
- mux.Handle()は、第2引数にHandlerをとりますから、HandlerFunc(handler)はServeHTTPを実装していなければなりません。
- このことから、元々ServeHTTPを実装していないhandlerが、HandlerFunc(handler)によって、ServeHTTPを実装しHandlerとして振る舞えるようになっていることがわかります。
- このHandlerFuncが何者なのかというのを次に見ていきます。
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
net/http.HandlerFunc
- HandlerFuncはFunc型の定義です。つまり、先ほどのHandlerFunc(handler)は型変換ということになります。
- HandlerFuncはServeHTTPというメソッドで自身を呼び出します。
- つまり、HandlerFunc(handler)という型変換によって、handlerはHandler interfaceとして振る舞える別の関数となります。
$ go doc net/http.HandlerFunc
type HandlerFunc func(ResponseWriter, *Request)
The HandlerFunc type is an adapter to allow the use of ordinary functions as
HTTP handlers. If f is a function with the appropriate signature,
HandlerFunc(f) is a Handler that calls f.
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)
ここまでのまとめ
- Goでサーバを立てるにはServer構造体を初期化しなければならない。
- Server構造体の主なメンバとして、IPアドレスとポート番号を組み合わせた文字列であるAddrと、リクエストに対する処理を登録するHandlerがある。
- Handlerは、ServeHTTPというメソッドのみを含むinterfaceである。ServeHTTPがHTTPリクエストからHTTPレスポンスを返す処理を実装する。
- ServeMuxというHandlerを利用することで、特定のパスに対応するHandlerを複数登録しておいて、リクエストのURLパターンによってHandlerを振り分けることができる。
- ServeMuxのHandleというメソッドは、第一引数にURLパターンを、第二引数にHandlerをとり、特定のパスに対応するHandlerを登録するときに使われる。
- ServeMuxのHandleFuncメソッドは、機能としてはHandleメソッドと同じである。異なるのは、ServeHTTPメソッドを実装していない、つまりHandlerではない関数を、Handlerとして登録することができるという点である。
- HandleFuncメソッドは、内部で第二引数にとった関数のHandlerFunc型への変換をおこない、Handlerとして振る舞えるようにして、それをServeMux.Handleで登録する。
net/http.DefaultServeMux
- httpパッケージにはServeMuxのインスタンスが一つ、DefaultServeMuxという名前で存在している。これは便宜のためである。
- Server構造体は、Handlerが登録されなければ、このDefaultServeMuxにリクエストを振る。
net/http.Handle
- ServeMuxのHandleメソッドに対応するのが、DefaultServeMuxにHandlerを登録するhttp.Handleである。
$ go doc net/http.Handle
func Handle(pattern string, handler Handler)
Handle registers the handler for the given pattern in the DefaultServeMux.
The documentation for ServeMux explains how patterns are matched.
net/http.HandleFunc
- ServeMuxのHandleFuncに対応するのが、DefaultServeMuxにHandlerではない関数をHandlerに変換して登録するhttp.HandleFuncである。
$ go doc net/http.HandleFunc
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
HandleFunc registers the handler function for the given pattern in the
DefaultServeMux. The documentation for ServeMux explains how patterns are
matched.
まとめ
この記事では、Goの標準ライブラリnet/httpに出てくる紛らわしい型名・関数名を整理しました!
net/httpではinterfaceによる抽象化によって拡張性を持たせた設計になっているので、HTTPサーバを表現力高く実装できます。
またDefaultServeMuxというインスタンスがあることで、単純なHTTPサーバをより簡潔に書くことができます。
一方、そのせいで紛らわしい型名・関数名が存在しています。それぞれの役割を正しく認識することで表現力の高さや簡潔さの恩恵を受けましょう!