現象
以下のようなコードでクエリパラメータをエンコードしたい、とする。
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("key", "a+b");
String u = UriComponentsBuilder.fromHttpUrl("http://www.google.com").queryParams(params).build().encode().toString();
System.out.println(u);
上記コードの結果は、以下表のように、spring-webのバージョンによって異なる。
spring-webのバージョン | 結果 |
---|---|
spring-boot1(spring-web-4.3.18.RELEASE) | http://www.google.com?key=a%2Bb |
spring-boot2(spring-web-5.0.4.RELEASE) | http://www.google.com?key=a+b |
新しい5.0では"+"がエンコードされなくなっている。
原因
https://stackoverflow.com/questions/50432395/whats-the-proper-way-to-escape-url-variables-with-springs-resttemplate-when-ca あたりに書いてあるが、クエリパラメータのエンコードの実装がRFC 3986ベースに変わったから、とかなんとか。
実装を確認する。HierarchicalUriComponentsというクラスが実際にエンコードを担当しており、その内部にエンコード対象かどうかを判定する箇所がある。
4.3ではこうなっている。
QUERY_PARAM {
@Override
public boolean isAllowed(int c) {
if ('=' == c || '+' == c || '&' == c) {
return false;
}
else {
return isPchar(c) || '/' == c || '?' == c;
}
}
},
5.0ではこうなっており、"+"が外れているのが分かる。
QUERY_PARAM {
@Override
public boolean isAllowed(int c) {
if ('=' == c || '&' == c) {
return false;
}
else {
return isPchar(c) || '/' == c || '?' == c;
}
}
},