URLに応じた処理(マルチプレクサとハンドラ)
Go標準ライブラリのnet/http
パッケージを利用して、リクエストをURL毎に処理する方法を説明します。
net/http
パッケージには、Webサーバの中核をなす機能として『マルチプレクサ』と『ハンドラ』があります。
それぞれの役割は次の通りです。
- マルチプレクサ:リクエストを受け付け、URLに対応するハンドラへ転送する
- ハンドラ:クリエストに応じた処理をして、レスポンスを返却する
+----------+ +--------------------------------------+
| | | |
| Client | | Server |
| | | |
| | | +-------------+ +----------+ |
| | | | | | | |
| | +------> | | Multiplexer | +-+-> | Handler1 | |
| | | | | | | | |
| | | +-------------+ | +----------+ |
| | | | |
| | | | |
| | | | +----------+ |
| | | | | | |
| | | +-> | Handler2 | |
| | | | | |
| | | +----------+ |
| | | |
+----------+ +--------------------------------------+
マルチプレクサ
マルチプレクサの使い方は2通りあります。
net/http
パッケージが用意したデフォルトのマルチプレクサを利用する- マルチプレクサを作成する
デフォルトのマルチプレクサ
次はデフォルトのマルチプレクサを利用して、URLに応じた処理をするサンプルです。
package main
import (
"fmt"
"net/http"
)
func main() {
// URLに対応する処理を登録
http.HandleFunc("/", defaultRoute)
http.HandleFunc("/route1", route1)
http.HandleFunc("/route2", route2)
server := http.Server{
Addr: ":8080",
// デフォルトのマルチプレクサを利用する場合はnilとする
Handler: nil,
}
server.ListenAndServe()
}
// リクエストを受け付ける処理(ハンドラ)
func defaultRoute(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is default route.")
}
// リクエストを受け付ける処理(ハンドラ)
func route1(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is /route1.")
}
// リクエストを受け付ける処理(ハンドラ)
func route2(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is /route2.")
}
このプログラムのポイントは次の通りです。
http.HandleFunc
関数を用いて、マルチプレクサにURLと対応する処理(ハンドラ)を登録するhttp.Server
のHandler
フィールドにnil
を指定する
なお、http.HandleFunc
関数のURL指定は末尾が/
かどうかで振る舞いが変わります。
末尾が/
でない場合、URLが完全一致した場合にリクエストが転送されます。
末尾が/
である場合、末尾/
を除いたURLが前方一致した場合にリクエストが転送されます。
たとえば、http.HandleFunc("/route/", handler)
は次のURLに対応します。
/route
/route/
/route/a
Webサーバを起動して、Webブラウザで次のURLにアクセスし、URL毎に表示が変わることを確認します。
http://localhost:8080/
http://localhost:8080/route1
http://localhost:8080/route2
マルチプレクサの作成
次は明示的にマルチプレクサを作成するサンプルです。 デフォルトのマルチプレクサで示したサンプルと同等の処理です。
package main
import (
"fmt"
"net/http"
)
func main() {
// マルチプレクサを作成
mux := http.NewServeMux()
// URLに対応する処理を登録
mux.HandleFunc("/", defaultRoute)
mux.HandleFunc("/route1", route1)
mux.HandleFunc("/route2", route2)
server := http.Server{
Addr: ":8080",
// 作成したマルチプレクサを指定
Handler: mux,
}
server.ListenAndServe()
}
// リクエストを受け付ける処理(ハンドラ)
func defaultRoute(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is default route.")
}
// リクエストを受け付ける処理(ハンドラ)
func route1(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is /route1.")
}
// リクエストを受け付ける処理(ハンドラ)
func route2(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is /route2.")
}
デフォルトのマルチプレクサとの違いは次の通りです。
http.NewServeMux
関数でマルチプレクサを作成するmux.HandleFunc
メソッドでマルチプレクサにURLと対応する処理(ハンドラ)を登録するhttp.Server
のHandler
フィールドに作成したマルチプレクサを登録する
デフォルトのマルチプレクサもhttp.NewServeMux
関数で作成したマルチプレクサも機能としての違いはないため、
マルチプレクサを作成・設定する処理は現時点で無駄に思えるでしょう。
ではなぜこれを扱ったかというと、より高機能なリクエストの振り分けが必要になり、サードパーティーや自作のマルチプレクサを利用する場合にマルチプレクサの設定が必要になるためです。
ハンドラ
ハンドラの作成方法は2通りあります。
- ハンドラ(
http.Handler
型)を実装する - ハンドラ関数(
http.HandlerFunc
型)を実装する
次の2つはハンドラと呼ばれるので、どちらを指すかは文脈で判断してください。
- ハンドラ(
http.Handler
型)とハンドラ関数(http.Handler
型)の総称 - ハンドラ(
http.Handler
型)
ハンドラ(http.Handler型)
ハンドラ(http.Handler
型)はServeHTTP
メソッドを持つインターフェースです。
引数はリクエストの読み込みとレスポンスの書き込みに使います。
- 第一引数:HTTPレスポンスを書き込むための情報
- 第二引数:HTTPリクエストを読み取るための情報
作成したハンドラをマルチプレクサに登録するため、
デフォルトのマルチプレクサではhttp.Handle
関数を使います。
マルチプレクサを作成した場合はHandle
メソッドを使います。
package main
import (
"fmt"
"net/http"
)
func main() {
// ハンドラを登録
http.Handle("/", handlerA{})
server := http.Server{
Addr: ":8080",
Handler: nil,
}
server.ListenAndServe()
}
type handlerA struct{}
// ハンドラを実装
func (h handlerA) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "handlerA")
}
ハンドラ関数(http.HandlerFunc型)
ハンドラ関数(http.HandlerFunc
型)はハンドラ(http.Handler
型)のServeHTTP
メソッドと同じシグネチャを持つ関数です。
作成したハンドラをマルチプレクサに登録するため、
デフォルトのマルチプレクサではhttp.HandleFunc
関数を使います。
マルチプレクサを作成した場合はHandleFunc
メソッドを使います。
package main
import (
"fmt"
"net/http"
)
func main() {
// ハンドラ関数を登録
http.HandleFunc("/", handlerFuncA)
server := http.Server{
Addr: ":8080",
Handler: nil,
}
server.ListenAndServe()
}
// ハンドラ関数
func handlerFuncA(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "handlerFuncA")
}
ハンドラを作り込むことで、リクエストの内容に応じて、動的にレスポンスを作成できます。
以降のページで、リクエストとレスポンスについて説明します。