基本のコード
マルチプレクサとして DefaultServeMux
を使用し、http.HandlerFunc()
でハンドラを付与していきます。
package main
import (
"fmt"
"net/http"
)
func hoge(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "hoge")
}
func fuga(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "fuga")
}
func main() {
server := http.Server{
Addr: ":8080",
Handler: nil,
}
http.HandleFunc("/hoge", hoge)
http.HandleFunc("/fuga", fuga)
server.ListenAndServe()
}
解説
構造体 http.Server
で Web サーバーを立ち上げる
構造体 http.Server
の値に、Web サーバーの設定を記述することができます。
そして ListenAndServe()
メソッドを呼び出し、Web サーバーを立ち上げます。
package main
import (
"net/http"
)
func main() {
server := http.Server{
Addr: ":8080",
Handler: nil,
}
server.ListenAndServe()
}
ハンドラを登録していないので、どこをアクセスしても 404 になります。
(厳密には Handler: nil
のとき DefaultServeMux
がハンドラとして使用されますが、DefaultServeMux
はマルチプレクサなので「どのパスにどのようなハンドラを結びつけるか」を記述しなくてはなりません。)
単一のハンドラを登録する
構造体 http.Server
のフィールド Handler
は、 http.Handler
型を期待します。
この http.Handler
はインターフェースです。
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
したがって、ServeHTTP(http.ResponseWriter, *http.Request
のシグネチャを持つメソッドにより http.Handler
を実装できます。
package main
import (
"fmt"
"net/http"
)
type HelloHandler struct{}
// *HelloHandler がインターフェース http.Handler を実装
func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, world!")
}
func main() {
// HelloHandler 型の変数を宣言
handler := HelloHandler{}
server := http.Server{
Addr: ":8080",
Handler: &handler,
}
server.ListenAndServe()
}
こうしてハンドラを登録できたので、localhost:8080 の任意のパスにアクセスすると、`Hello, world!" と表示されるようになります。
http.Handle()
で複数のハンドラを登録する
構造体 http.Server
のフィールド Handler
が nil
のとき、ハンドラとして DefaultServeMux
が使用されます。
以下のように、DefaultServeMux
は 構造体 ServeMux
型のポインタです。
type ServeMux struct {
// 省略
}
var defaultServeMux ServeMux
var DefaultServeMux = &defaultServeMux
そして、*ServeMux
は インターフェース http.Handler
を実装しています。
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
// 省略
}
すなわち、DefaultServeMux
は http.Handler
型です。
この DefaultServeMux
はマルチプレクサとして機能します。
関数 http.Handle()
を用いて DefaultServeMux
に複数のハンドラを付与していきます。
package main
import (
"fmt"
"net/http"
)
type HogeHandler struct{}
type FugaHandler struct{}
func (h *HogeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "hoge")
}
func (h *FugaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "fuga")
}
func main() {
hoge := HogeHandler{}
fuga := FugaHandler{}
server := http.Server{
Addr: ":8080",
Handler: nil, // DefaultServeMux を使用
}
// DefaultServeMux にハンドラを付与
http.Handle("/hoge", &hoge)
http.Handle("/fuga", &fuga)
server.ListenAndServe()
}
localhost:8080/hoge にアクセスすると "hoge"、localhost:8080/fuga にアクセスすると "fuga" と表示されます。
このとき、関数 http.Handle()
は DefaultServeMux
のメソッド Handle()
を呼び出しています。
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
http.HandleFunc()
で複数のハンドラを登録する
関数 http.HandleFunc()
を使用すると、もっと簡単にハンドラを登録できます。
関数 http.HandleFunc()
は第二引数に HandlerFunc
型の関数を期待します。
package main
import (
"fmt"
"net/http"
)
// HandlerFunc 型の関数を定義
func hoge(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "hoge")
}
// HandlerFunc 型の関数を定義
func fuga(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "fuga")
}
func main() {
server := http.Server{
Addr: ":8080",
Handler: nil, // DefaultServeMux を使用
}
// HandlerFunc を Handler に変換
// Handler を DefaultServeMux に付与
http.HandleFunc("/hoge", hoge)
http.HandleFunc("/fuga", fuga)
server.ListenAndServe()
}
関数 http.HandleFunc()
は DefaultServeMux
のメソッド HandleFunc()
を呼び出しています。
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
そして、DefaultServeMux
のメソッド HandleFunc()
は、 DefaultServeMux
のメソッド Handle()
を呼び出しています。
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
このとき、HandlerFunc(handler)
は http.Handler
を実装しています。
ここで HandlerFunc
型の定義を確認してみましょう。
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
HandlerFunc
型にはメソッド ServeHTTP(http.ResponseWriter, *Request)
が定義されています。
したがって HandlerFunc
型で関数をキャストすると、キャストした結果は http.Handler
を実装することになります。
ServeMux
によるルーティング
パスの末尾に /
がない場合、完全一致でルーティングされます。
パスの末尾に /
がある場合、以下のようなルーティングとなります。
package main
import (
"fmt"
"net/http"
)
func index(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "index")
}
func hoge(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "hoge")
}
func hogefuga(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "hogefuga")
}
func main() {
server := http.Server{
Addr: ":8080",
Handler: nil,
}
http.HandleFunc("/", index)
http.HandleFunc("/hoge/", hoge)
http.HandleFunc("/hoge/fuga/", hogefuga)
server.ListenAndServe()
}
入力パス | 結果 |
---|---|
/ | index |
/random | index |
/hoge | hoge |
/hoge/fuga | hogefuga |
/hoge/123/fuga | hoge |
/hoge/123/fuga/456 | hoge |
/hoge/fuga/456 | hogefuga |
また、/users/123/posts/456
のような URL からパスパラメータを取り出す機能はないので、自前で処理を書くか、他のライブラリやフレームワークを使用する必要があります。
参考
次回