JAX-RS Client の WebTarget.queryParam() の仕様にハマったのでメモ。
問題点
WebTarget.queryParam() の仕様はこちらです。
WebTarget に対してクエリパラメータを設定するというメソッドです。
このメソッド、URIエンコードについて注意点があります。
- パラメータ名・値ともに URIテンプレートが使用できる。
- 仕様には明記されていないが、たいていの実装ではパラメータ名・値ともに URI エンコードしてくれる。
URIテンプレートは {} で囲んだ部分にパスパラメータを突っ込んでくれるという機能ですが、よく考えるとちょっと問題があります。
それはクエリパラメータ値に "{" や "}" 自体を含めることができない、という問題です。"{", "}" ともにパスパラメータとして解釈されるためですね。
これを避けるためには、"{" や "}" を URIエンコードしなければなりません。それぞれ %7b, %7d にすれば良いということです。
が、JerseyやRESTEasyなどの実装では本メソッドは上記の通り URI エンコードをする動作なので、そのままもう一度URIエンコードされると %257b, %257d に変換されてしまうはずです。つまり { や } が使えないのでは、、、?
回避方法
Jersey 実装ではこれを回避するために URI エンコードを二重に実施しないようになっています。具体的には % の後ろに16進文字(0-9a-fA-F)が2個連続している場合は % をエンコードしないというルールです。なので、{, } をそのままエンコードしても大丈夫です。
また逆に考えると、たまたま % の後ろに16進文字が2個連続していたら % は URL エンコードしてくれないということでもあります。
従って、常に自分で URIエンコードしてから queryParam() を呼ぶようにしたほうがよいということです。
他の実装がどうなっているかは知りません。RESTEasy のコードをちょっと見た感じでは % は一切エンコードしないようになっていたみたいです。
当然ですが、上記動作は JAX-RS の仕様からは読み取れないです(実装依存)。なので一応動作確認してから使いましょう。