最近仕事で使い始めた Golang
( net/http
標準ライブラリ)とWebフレームワーク Gin
のリクエストハンドリングについてどのような仕組みで行っているのかをまとめてみます。
前提
動作確認環境は以下の通りです。
- Ubuntu 18.04.3 LTS
- go 1.13.6
- gin v1.5.0
Golang
マルチプレクサとは
GolangではユーザがWebページにアクセスされた際にマルチプレクサというものを使って、どのページを呼び出すかを決めます。
以下のように、内部構造にURLとページ(ハンドラ)を対応づける構造体を持っていてもので、アクセスされた時のURLから呼び出すページ(ハンドラ)が決まります。
goデフォルトのマルチプレクサ
実際に以下のようにコードを書いて、ローカルで http://localhost:8080/cart
のようにアクセスすると Cart page
と表示されるかと思います。
package main import ( "fmt" "net/http" ) func defaultHandler(writer http.ResponseWriter, request *http.Request) { fmt.Fprintf(writer, "Hello World! (default Page)") } func cartHandler(writer http.ResponseWriter, request *http.Request) { fmt.Fprintf(writer, "Cart page") } func main() { http.HandleFunc("/", defaultHandler) http.HandleFunc("/cart", cartHandler) http.ListenAndServe(":8080", nil) }
このように、 http.ListenAndServe()
メソッドなどでマルチプレクサが指定されていない(nil
の)場合は、マルチプレクサの構造体ServerMux
のインスタンスであるDefaultServerMux
がデフォルトで使われ、手前の行のhttp.HandleFunc()
で登録されたルーティングが処理されます。
[参照コード] go/server.go at bbbc6589dfbc05be2bfa59f51c20f9eaa8d0c531 · golang/go · GitHub
goカスタムのマルチプレクサ
マルチプレクサは、標準ライブラリであるnet/http
以外のものに変更可能です。
標準ライブラリでは、例えば http://localhost:8080/books/100 のように一部がIDになっているような可変のURLの場合に対応できないですが、HttpRouterのような別のマルチプレクサに置き換えることが実現できます。
package main import ( "fmt" "github.com/julienschmidt/httprouter" "net/http" ) func defaultHandler(writer http.ResponseWriter, request *http.Request, _ httprouter.Params) { fmt.Fprintf(writer, "Hello World! (default Page)") } func cartHandler(writer http.ResponseWriter, request *http.Request, _ httprouter.Params) { fmt.Fprintf(writer, "Cart page") } func main() { mux := httprouter.New() mux.GET("/", defaultHandler) mux.GET("/cart", cartHandler) http.ListenAndServe(":8080", mux) }
ListenAndServe()
には、HttpRouter
のインスタンスが引数に入っています。
Gin
Ginのマルチプレクサ
最後にGolangのWebフレームワークの一つであるGinのマルチプレクサに関してですが、前述のHttpRouterを独自に改良したものが使われているようです。changelogにcustom hand optimized HttpRouter for Ginのような記載があったり、コードの各所のHttpRouterのライセンスの記載がありました。
ginのコードを読んで見ると独自にカスタマイズした engine
(マルチプレクサ) を ListenAndServe()
の引数に渡しているの読み取れます。
[参照コード] gin/gin.go at master · gin-gonic/gin · GitHub
同様に、こちらも実際にコードを書いてみると
package main import ( "github.com/gin-gonic/gin" ) func defaultHandler(ginctx *gin.Context) { ginctx.String(200, "Hello World! (default Page)") } func cartHandler(ginctx *gin.Context) { ginctx.String(200, "Cart page") } func main() { mux := gin.Default() mux.GET("/", defaultHandler) mux.GET("/cart", cartHandler) mux.Run(":8080") }
のようになります。
Ginのマルチプレクサの特徴
HttpRouterとは異なり、ルーティングをグループ化できるという特徴があるようです。(こちらは未検証のため公式ドキュメントのコードを引用します)
func main() { router := gin.Default() // v1 のグループ v1 := router.Group("/v1") { v1.POST("/login", loginEndpoint) v1.POST("/submit", submitEndpoint) v1.POST("/read", readEndpoint) } // v2 のグループ v2 := router.Group("/v2") { v2.POST("/login", loginEndpoint) v2.POST("/submit", submitEndpoint) v2.POST("/read", readEndpoint) } router.Run(":8080") }
細かいルーティングの指定ができて良さそうです。