RestTemplateで、「+」をURLエンコードしてリクエストする方法

こんにちは。 エキサイト株式会社の三浦です。

URLでは、使えない・特殊な意味を持つ文字があります。 そういった文字を使いたい場合はURL用のエンコードを掛けてからリクエストをするのですが、RestTemplateではデフォルトでは「+」がエンコードされないという問題があります。

今回は、その問題を解決する方法を紹介します。

URLの特殊文字

URLとは、例えば 「https://tech.excite.co.jp/ 」のような文字列です。 このURLでは、使えなかったり特殊な意味を持つ文字が存在します。

例えば、日本語をそのまま使うことはできません。 「https://サンプル/」のようなURLを使いたい場合は、URLエンコードを掛けて「https://%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB/」のようにする必要があります。

また、URL上で特殊な意味を持つ文字もあります。 上記でも使われている「:」や「/」などの文字は、URL上の区切りなどで利用されるために、特殊な意味を持たないただの文字として使いたい場合はエンコードする必要があります。 「https://aaa/bbb/」のURLで、間に挟まれている「/」を特殊な意味を持たない文字として扱いたい場合は、「https://aaa%2Fbbb/」とする必要があります。

そして、そういったエンコードが必要な文字には「+」も含まれています。

RestTemplateとURLエンコード

RestTemplateでリクエストを送る場合は、下記のように送る場合が多いと思います。

// 文字列をもとにURLを作成(エンコードもする)
URI uri = UriComponentsBuilder.fromUriString("https://sample/")
    .queryParam("sample_query", "sample+")
    .build()
    .encode()
    .toUri();

// 作ったURLを使ってGETリクエストを送信
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
    uri,
    HttpMethod.GET,
    null,
    String.class
)

この場合、 UriComponentsBuilder がURLエンコードを掛けてくれます。 ただ、実は落とし穴があり、「+」の文字がエンコードされません。

実は歴史的経由があり、「+」はそのまま使える文字として認識されているようです。 ですが、リクエストを受け取る側の処理によっては「+」がエンコードされている必要があるので、このままでは問題が生じる可能性があります。

RestTemplateで「+」をエンコードする方法

では、どのようにして「+」をエンコードすればよいのでしょうか。

いくつか方法があるのですが、例えば

// 文字列をもとにURLを作成(エンコードもする)
URI uriWithNoEscapedPlus = UriComponentsBuilder.fromUriString("https://sample/")
    .queryParam("sample_query", "sample+")
    .build()
    .encode()
    .toUri();

// 「+」を文字置換でエンコードする
String strictlyEscapedQuery = StringUtils.replace(uriWithNoEscapedPlus.getRawQuery(), "+", "%2B");

// エンコード後のクエリに置き換える
URI uri = UriComponentsBuilder
    .fromUri(uriWithNoEscapedPlus)
    .replaceQuery(strictlyEscapedQuery)
    .build(true)
    .toUri();

// 作ったURLを使ってGETリクエストを送信
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
    uri,
    HttpMethod.GET,
    null,
    String.class
)

このようにすればエンコードが可能です。 これ以外にも RestTemplate の途中に同様の処理を挟むようInterceptorを使うという方法もあります。

処理の本質(文字置換をする)は同じなので、お好みの方でやってもらえればと思います。

最後に

「+」がURLで使用される場面はそこまで多くはないため、引っかかってしまうとかなり詰まってしまうこともあると思います。 そういった場合に、参考になれば幸いです。