はじめに
Go1.22リリース連載 の5本目です。
本記事ではGoの標準ライブラリである net/http の ServeMux におけるルーティング周りの強化について取り上げます。
関連する Release Note と Issue はこちらを参照してください。
https://tip.golang.org/doc/go1.22#enhanced_routing_patterns
https://github.com/golang/go/issues/61410
変更点
HTTPメソッドの指定が可能に
ServeMux.Handle や ServeMux.HandleFunc を使用してハンドラを登録する際に GET /xxx
のようにHTTPメソッド指定して、ハンドラを呼び分けることができるようになりました。
mux := http.NewServeMux() |
従来はハンドラの中で、自前でHTTPメソッドによって処理を呼び分ける(もしくは chi のようなHTTPメソッドの呼び分けに対応したルーティングライブラリを利用する)必要がありました。
mux := http.NewServeMux() |
HTTPメソッドの指定について、3点ほど詳細を補足しておきます。
- メソッド指定のパターン(
GET /hello
)とメソッドを指定しないパターン(/hello
)の両方が登録された場合は、メソッド指定のハンドラが優先されます。 - ハンドラをGETメソッドで登録した場合は、自動的にHEADメソッドでも登録されます。
- 存在しない(標準仕様として定められていない)HTTPメソッドを指定した場合でも特に起動時のエラーは発生しません(ちなみにパスに
"HOGE /"
を指定した場合curl -X HOGE ...
で呼び出すことが可能です)。
ワイルドカードの指定が可能に
HTTPメソッドが指定できるようになっただけでなく、/items/{id}
のようにワイルドカードが使用できるようになりました。ワイルドカードにマッチしたパスセグメントの値については Request.PathValue でアクセスできます。
mux := http.NewServeMux() |
また、/files/{path...}
のように ...
で終わるワイルドカードを指定することで、特定のパスに続く全てのセグメントにマッチさせることができます。
mux := http.NewServeMux() |
従来は /items/{id}
のようなパスパラメータに対するワイルドカードマッチを実現するためには、/items/
のように末尾スラッシュをつけることで、/items/
で始まる全てのリクエストをハンドリングし、ハンドラの中で、自前でパスパラメータをパースする必要がありました。
mux := http.NewServeMux() |
Go1.22においても末尾にスラッシュを指定した場合の挙動(指定したパスで始まる全てのエンドポイントがハンドリングされる挙動)は従来と変わりませんが、末尾スラッシュで終わるパスのみに完全にマッチさせたい場合は /items/{$}
のように {$}
を付与することで完全なパターンマッチを実現できます。
mux := http.NewServeMux() |
後方互換性について
https://pkg.go.dev/net/http@go1.22rc2#hdr-Compatibility
ほとんどのユースケースにおいて問題になることはないと思いますが、パスに {}
を使用していたケースなどでは問題が起きるかもしれません。
例えば、ワイルドカードが存在しなかった従来のバージョンでは下記のように /items/{hoge}
と items/{fuga}
は別々のパスとして認識され、異なるハンドラを登録し、呼び分けることができました。
mux := http.NewServeMux() |
しかしながらGo1.22では {}
で囲んだ部分はワイルドカードとなるため、/items/{hoge}
と items/{fuga}
は同一のパターンと認識され、起動時に panic となります。
panic: pattern "/items/{fuga}" (registered at xxxxx) conflicts with pattern "/items/{hoge}" (registered at xxxxx): |
もしワイルドカードなどの新しい仕様が許容できない場合は、環境変数に GODEBUG=httpmuxgo121=1
を設定することで、Go1.22を使いつつ以前の動作のまま動かすことが可能です。
性能計測
Go標準のベンチマーク機能を用いて ServeMux とルーティング機能を提供する主要ライブラリである chi と Gorilla の性能を比較してみます。
シナリオ
各ライブラリを利用して以下の4パターンのAPIを定義し、それぞれ ServeHTTP をテストする形で性能を計測します。
- パスパラメータが存在しないケース
- パスパラメータが1個存在するケース
- パスパラメータが5個存在するケース
- パスパラメータが10個存在するケース
ソースコードはこちらで公開してますが、各ハンドラ内ではパスパラメータ値の取得まで行っております。
実行結果
実行結果に表示されている各ケースの命名については、Benchmark${ライブラリ名}${パスパラメータ数}
(例. BenchmarkServeMux0)となります。
結果を見ると、パスパラメータが存在しない or 1個のみの場合は ServeMux > chi > Gorilla となり、パスパラメータが5個、10個と増えた場合は chi > ServeMux > Gorilla となっています。
go test -bench . -benchmem |
一般的にAPIのパスパラメータはそこまで多くならない(通常は0 ~ 2個)よう設計されることが多いと思います。
ルーティングライブラリを導入せず、標準の ServeMux だけで十分というケースが増えそうですね。