こんにちは。 エキサイト株式会社の三浦です。
URLには、特殊な意味を持っていたり使えなかったりする文字があり、それらを使いたい場合はURLエンコードを掛ける必要があります。 そのため、とある条件下ではJavaのRestTemplateは自動的にURLエンコードを掛けてくれます。
ですが、場合によってはエンコードされてしまうと問題がある場合もあります。
今回は、RestTemplateで意図せずURLエンコードさせないための注意点を紹介します。
URLエンコードをすると問題があるパターン
URLには、特殊な意味があったり使えない文字があります。 例えば日本語は、使えない文字の1つです。 日本語を使いたい場合は、以下のように変換する必要があります。
// これを https://サンプル // こうする https://%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB
これでURLとして使えるようになるのですが、実はこの %
はURLで特殊な意味を持つ文字となっています。
そのため、うっかりもう一回URLエンコードを掛けてしまうと、以下のように更に変換されてしまいます。
// これが https://%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB // こうなる https://%25E3%2582%25B5%25E3%2583%25B3%25E3%2583%2597%25E3%2583%25AB
この状態だと受け取り側も2回デコードを掛ける必要が出てきてしまうため、避ける必要があります。
RestTemplateとエンコード
ところで、JavaでURLリクエストをする際は、RestTemplateを使うという方が多いと思います。 例えば、以下のように使うことができます。
// 1. URLエンコードを掛けた上でURL文字列を作成 String uriString = UriComponentsBuilder.fromUriString("https://sample") .queryParam("sample_query", "サンプル") .build() .encode() .toUriString(); // 2. 作ったURL文字列を使ってGETリクエストを送信 RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.exchange( uriString, HttpMethod.GET, null, String.class );
ただし、これだと実は問題があります。
まず、 1. URLエンコードを掛けた上でURL文字列を作成
をした結果、以下のような文字列が発行されます。
// 1. URLエンコードを掛けた上でURL文字列を作成 String uriString = UriComponentsBuilder.fromUriString("https://sample") .queryParam("sample_query", "サンプル") .build() .encode() .toUriString(); // uriString = https://sample?sample_query=%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB
これは正しくURLエンコードされた文字列になります。
しかし、実際に 2. 作ったURL文字列を使ってGETリクエストを送信
にて送信されるURLは以下になります。
// 2. 作ったURL文字列を使ってGETリクエストを送信 RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.exchange( uriString, HttpMethod.GET, null, String.class ); // 送信されたURL : https://sample?sample_query=%25E3%2582%25B5%25E3%2583%25B3%25E3%2583%2597%25E3%2583%25AB
見ての通り、二重エンコードされています。
実は文字列を渡して restTemplate.exchange
を実行すると、自動的にURLエンコードが掛けられます。
もちろん有用な場合もありますが、知らずに使っていると上記のように意図しないエンコード結果になってしまうでしょう。
RestTemplateで自動的にエンコードさせない方法
では、どうすれば自動エンコードさせないようにできるのでしょうか。 実は、文字列ではなくURI型で渡せばエンコードされずにリクエストが実行されます。
// 1. URLエンコードを掛けた上でURIを作成 URI uri = UriComponentsBuilder.fromUriString("https://sample") .queryParam("sample_query", "サンプル") .build() .encode() .toUri(); // 2. 作ったURIを使ってGETリクエストを送信 RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.exchange( uri, HttpMethod.GET, null, String.class );
このようにすれば、作成したURIそのままでリクエストが実行されます。
最後に
意図しないURLエンコードは、当然ですが意図しないリクエストにつながる恐れがあります。
基本的にはRestTemplateにはURI型を渡すようにし、エンコードをかける際は UriComponentsBuilder
で明示的に実行するのが良いでしょう。