Goのnet/httpパッケージを使って簡単な通信をテストしてみる。
ともかく動かす
前回作成したプロジェクト用ディレクトリにmain.go
という名前でソースコードを作成する。
package main
import (
"fmt"
"net/http"
)
func main() {
mux := http.NewServeMux()
handler := new(MyHandler)
mux.Handle("/A", handler)
mux.HandleFunc("/B", MyFunc)
http.ListenAndServe(":8080", mux)
}
type MyHandler struct{}
func (handler *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "A")
}
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
という関数らしい。
公式ドキュメントのソースを見てみると、
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
という関数を呼び出している。
こちらも公式ドキュメントを見てみると、
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
を引数として関数を呼び出しているように見えるが、
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
このように定義されているHandlerFunc型にキャストしている。
HandlerFunc
型にはServeHTTP
が定義されており、その処理としてはHandlerFunc
(自分自身)を呼び出すような内容になっている。
ServeHTTP
が定義されているということはHandler
インターフェースを満たしていることになる。
これにより、ただの関数がHandle
関数でハンドラーとして登録できるようになっている。
簡単なリクエスト処理を書いてnet/httpパッケージの学習を行った。
今回は公式のServeMux
を見ていったが、これを他パッケージのmuxなどで置き換えることでさらに強力なルーティングなども行えるらしい。
これらも順次調べていきたい。