golang勉強するンGo(3) ~ リクエスト処理
Goのnet/httpパッケージを使って簡単な通信をテストしてみる。
ともかく動かす
前回作成したプロジェクト用ディレクトリにmain.go
という名前でソースコードを作成する。
package main import ( "fmt" "net/http" ) func main() { mux := http.NewServeMux() // A handler := new(MyHandler) mux.Handle("/A", handler) // B mux.HandleFunc("/B", MyFunc) // サーバ起動 http.ListenAndServe(":8080", mux) } // MyHandler : パターンA(構造体を定義) type MyHandler struct{} func (handler *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "A") } // MyFunc : パターンB(関数を定義) func MyFunc(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "B") }
パターンAとパターンBの二つの方法でリクエストに対する応答を作成してみた。
このコードを保存して$ go run main.go
で実行する。
http://localhost:8080/Aとhttp://localhost:8080/Bにアクセスしてみると、それぞれA
・B
と表示される。
解説
関数main
の最後でhttp.ListenAndServeを呼び出している。
この関数により第一引数に指定した8080ポートでサーバが起動する。
第二引数に渡したmux
(type ServeMux
)はHTTP request multiplexer
とかいうもの。
リクエストに応じたハンドラーを呼び出すらしいが、よくわからん。
とにかくこのServeMux
にURLと呼び出したいハンドラーを登録すればリクエストを処理できるみたいだ。
で、ServeMux
に関数を登録するのがHandle
という関数らしい。
公式ドキュメントのソースを見てみると、
// Handle registers the handler for the given pattern. // If a handler already exists for pattern, Handle panics. func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() if pattern == "" { panic("http: invalid pattern") } if handler == nil { panic("http: nil handler") } if _, exist := mux.m[pattern]; exist { panic("http: multiple registrations for " + pattern) } if mux.m == nil { mux.m = make(map[string]muxEntry) } mux.m[pattern] = muxEntry{h: handler, pattern: pattern} if pattern[0] != '/' { mux.hosts = true } }
ごちゃごちゃと書いてあるが、色々とチェックをした後にpattern
とhandler
をマップに対応づけている。
pattern
はURLのようなものだとイメージできるが、このHandler
型のhandler
は何者なのか?
こちらも公式ドキュメントをみると、
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
どうやらインターフェースのようだ。
ServeHTTP(ResponseWriter, *Request)
を実装しているものであればHandler
として扱える。
そこで今回書いたmain.go
に戻って見てみると、パターンAで使用しているstructのMyHandler
はたしかにServeHTTP
を実装している。
これによりHandler
インターフェースを満たしたことになるのでハンドラーとして登録できる。
では、パターンBの方も同様に見ていこう。
パターンBの方ではHandleFunc
という関数を呼び出している。
こちらも公式ドキュメントを見てみると、
// HandleFunc registers the handler function for the given pattern. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if handler == nil { panic("http: nil handler") } mux.Handle(pattern, HandlerFunc(handler)) }
パターンの文字列とhandler func(ResponseWriter, *Request)
を引数にとり、handler
に対してHandlerFunc
を噛ませてから先ほど同様にHandle
で登録するような処理の流れだ。
このHandlerFunc
の部分は一見、handler
を引数として関数を呼び出しているように見えるが、
// 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. type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }
このように定義されているHandlerFunc型にキャストしている。
HandlerFunc
型にはServeHTTP
が定義されており、その処理としてはHandlerFunc
(自分自身)を呼び出すような内容になっている。
ServeHTTP
が定義されているということはHandler
インターフェースを満たしていることになる。
これにより、ただの関数がHandle
関数でハンドラーとして登録できるようになっている。
簡単なリクエスト処理を書いてnet/httpパッケージの学習を行った。
今回は公式のServeMux
を見ていったが、これを他パッケージのmuxなどで置き換えることでさらに強力なルーティングなども行えるらしい。
これらも順次調べていきたい。