Spring の UriComponentsBuilder のちょっと罠っぽいところ

UriComponentsBuilder は以下のように、URIの構築を簡単にできるやつです。 Spring とか使っている人だと使ったことあると思います。

String result = UriComponentsBuilder
        .fromUriString("https://example.com/")
        .queryParam("hoge", "あ")
        .toUriString();

assertThat(result).isEqualTo("https://example.com/?hoge=%E3%81%82");

最近使っていて気づいたのですが、UriComponentsBuilder.fromUriString(str) or UriComponentsBuilder.fromHttpUrl(str) に渡す URI string に query がついている場合、その部分も encode されるという挙動をします。 つまり、予め encodeしていると2重に encode されてしまうわけです。

こんな感じです。

String result = UriComponentsBuilder
        .fromHttpUrl("https://example.com/?nextPath=%2Fpath%2Fto%2Fsome%2Fpage") // "/path/to/some/page"
        .queryParam("hoge", "あ")
        .toUriString();

assertThat(result).isNotEqualTo(
        "https://example.com/?nextPath=%2Fpath%2Fto%2Fsome%2Fpage&hoge=%E3%81%82");
assertThat(result).isEqualTo(
        "https://example.com/?nextPath=%252Fpath%252Fto%252Fsome%252Fpage&hoge=%E3%81%82");

んー。個人的には、queryParam で指定した部分だけのほうが直感的だと思いますが...。

参考までに、Apache HttpClient にある URIBuilder だと query 部分が2重にencodeされるということはありません。

String result = new URIBuilder("https://example.com/?nextPath=%2Fpath%2Fto%2Fsome%2Fpage")
        .addParameter("hoge", "あ")
        .build().toString();
assertThat(result).isEqualTo(
        "https://example.com/?nextPath=%2Fpath%2Fto%2Fsome%2Fpage&hoge=%E3%81%82");