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/Ahttp://localhost:8080/Bにアクセスしてみると、それぞれABと表示される。

解説

関数mainの最後でhttp.ListenAndServeを呼び出している。 この関数により第一引数に指定した8080ポートでサーバが起動する。

第二引数に渡したmuxtype ServeMux)はHTTP request multiplexerとかいうもの。 リクエストに応じたハンドラーを呼び出すらしいが、よくわからん。

とにかくこのServeMuxURLと呼び出したいハンドラーを登録すればリクエストを処理できるみたいだ。

で、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
    }
}

ごちゃごちゃと書いてあるが、色々とチェックをした後にpatternhandlerをマップに対応づけている。

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などで置き換えることでさらに強力なルーティングなども行えるらしい。 これらも順次調べていきたい。