net/httpパッケージを利用して、HTTPサーバーを起動してみます。
HandleFunc関数
まずはHandleFunc関数を利用した最も一般的でシンプルな実装を行い、Handler(インターフェイス)およびListenAndServe関数、また背後で機能するServeMux等を説明していきます。
HandleFunc関数の機能を掘り下げていくと、Go言語におけるサーバー実装の基本をより理解できるようになります。
http://localhost:8080/hello にアクセスすると、「Hello, World」と表示されるコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package main import ( "fmt" "net/http" ) func helloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Hello, World</h1>") } func main() { http.HandleFunc("/hello", helloHandler) fmt.Println("Server Start") http.ListenAndServe(":8080", nil) } |
HandleFunc関数の第1引数にはURLのパスを、第2引数にはそのパスに対して実行される関数を指定しています。上記では、/helloにアクセスした際に、helloHandler関数が実行されるように設定しています。
HandleFunc関数を説明する前に、Handlerについて確認しておきます。
HandlerとはServeHTTPという関数を定義したインターフェイスです。
1 2 3 |
type Handler interface { ServeHTTP(ResponseWriter, *Request) } |
8〜10行目のhelloHandler関数には、ResponseWriterとhttp.Requestの2つの引数がありますが、これはHandlerインターフェイスにおけるServeHTTP関数と同様のシグネチャを構成しています。
HandleFunc関数は下記のように定義されています。第2引数に関数を渡すと、DefaultServeMux.HandleFuncに登録されます。上述したコードで言えばHandlerのServeHTTP関数と同様のシグネチャを持つ関数、helloHandler関数を渡すことができます。
1 2 3 |
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) } |
DefaultServeMuxはグローバルなServeMuxのインスタンスです。つまりnet/httpパッケージにおいてあらかじめDefaultServeMuxが用意されています。ServeMuxはリクエストマルチプレクサと呼ばれ、URLとHandlerの関連付けをおこなうルーターとしての役目を持ちます。
ServeMux型のHandleFuncメソッドの定義をみていきます。第2引数で関数を渡すとHandlerFunc(2行目)に渡されます。このHandlerFuncは型であり、適切なシグネチャを持った関数をHandlerに変換することができます。
1 2 3 |
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler)) } |
更にHandlerFunc型の定義を見てみると、ServeHTTPメソッドを持ちServeMuxもHandlerであることが分かります(すわなちDefaultServeMuxもHandler)。また自身のHandlerFuncをfとして呼び出していることも確認できます。
1 2 3 4 5 |
type HandlerFunc func(ResponseWriter, *Request) func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } |
上記までの説明をざっくりまとめると、HandleFunc関数によってHandlerのServeHTTP関数と同様のシグネチャを持つ関数はHandlerに変換され、またそのHandlerがDefaultServeMuxに登録されます。
1 |
http.ListenAndServe(":8080", http.DefaultServeMux) |
1 2 3 4 5 6 |
func main() { mux := http.NewServeMux() mux.HandleFunc("/hello", helloHandler) fmt.Println("Server Start") http.ListenAndServe(":8080", mux) } |
はじめに提示したコードのようにhttp.HandleFunc関数を利用すればnilを指定することができ、よりシンプルな記述であったことがわかります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package main import ( "fmt" "net/http" ) type helloHandler struct{} func (*helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Hello, World</h1>") } func main() { handler := &helloHandler{} http.Handle("/hello", handler) fmt.Println("Server Start") http.ListenAndServe(":8080", nil) } |
HandleFunc関数とは違い、Handle関数の第2引数には関数を指定しません。helloHandler型のServeHTTP関数を定義したHandlerのポインタを指定しています(15・16行目)。