【Go】配列のURLクエリパラメータを渡す推奨パターン
概要
HTTPのリクエストで配列のURLクエリパラメータ(クエリストリング / クエリ文字列)を渡す場合に、Goのnet/httpにおける推奨パターンを考察します。
環境
- go 1.21.4
配列のURLクエリパラメータを受け渡すパターン
RFC3986 - Uniform Resource Identifier (URI): Generic Syntaxでは、配列のURLクエリパラメータを受け渡すパターンに関して明確に定義されていません。
言語やフレームワークごとに以下のようなパターンで受け渡しされることが多いようです。
?hobbies=programming&hobbies=sports
?hobbies[]=programming&hobbies[]=sports
?hobbies[1]=programming&hobbies[2]=sports
?hobbies=programming,sports
cf. How to pass an array within a query string? - Stack Overflow
Goのnet/httpにおけるパターン
Goのnet/httpでは、URLクエリパラメータは以下のように取得可能です。
import (
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
// クエリパラメータを取得
queryParams := r.URL.Query()
...
cf. https://pkg.go.dev/net/url#URL.Query
このとき、返り値としてValuesが返却されますが、このValuesの型は map[string][]string
です。
これはURLクエリパラメータが配列か単一かに限りません。
このValuesから任意のURLクエリパラメータを取得するには、以下のようにmapのキーに関連付けられた値を取得するGoの一般的な記法を利用するか、
hobbies, ok := queryParams["hobbies"]
一致した最初のURLクエリパラメータの取得だけでよければ、提供されているGet を利用します。
limit := queryParams.Get("limit")
上記のnet/httpの設計に沿い、実装コストが少なくよりシンプルに書ける方法を考えると、
?hobbies=programming&hobbies=sports
のパターンが最適だと思います。
2のパターンでも下記のように記述するだけなのであまり違いはないですが、そもそもPHPの記法なのと命名を複数形にすれば配列であることは自明のため、[]
を付与することはGoのnet/httpにおいては余計でありシンプルさに欠けます。
hobbies, ok := queryParams["hobbies[]"]
エビデンスとしては心許ないですが、Query()
のユニットテストで bar
のように複数の値を渡しているのでGo側も1のパターンを想定していそうです。
func TestQueryValues(t *testing.T) {
u, _ := Parse("http://x.com?foo=bar&bar=1&bar=2&baz")
v := u.Query()
cf. https://cs.opensource.google/go/go/+/refs/tags/go1.21.4:src/net/url/url_test.go;l=130
実際にはクライアントとの兼ね合いもあるため、サーバーだけの理由では決められませんが特にクライアント側の合理的な要望がなければ1のパターンを採用するのがよさそうです。
Discussion