🕳️

Java WebClientで'+'(プラス記号)がスペースに置き換わってしまったときの対処法

2022/08/10に公開

概要

Java Spring BootのWebClientに'+'記号を含むクエリストリングを渡すと、スペースとして解釈されてしまった

  • 例)元のURL
http://localhost:8080/sample/?date=2022-08-01T11:22:33+09:00
  • WebClientによって変換されたURL
http://localhost:8080/sample/?date=2022-08-01T11:22:33 09:00

これでは、URLをデコードしてもスペースは+に変換されず元のURLに戻すことが出来ない

  • 期待していたURL
http://localhost:8080/sample/?date=2022-08-01T11:22:33%2B09:00

プラス記号をURLエンコードすると本来は%2Bとなる。

そこで、WebClientのエンコード前後にプラス記号を%2Bにエンコードするインターセプターを挟むことで対処できたので、本記事その方法を紹介する。

対象

  • Spring Bootフレームワークが提供するWebClientの利用を想定している
  • RestTemplate利用時の対処法は下記サイトを参照

https://stackoverflow.com/questions/54294843/plus-sign-not-encoded-with-resttemplate-using-string-url-but-interpreted

対処法

RestTemplateでは、interceptorsメソッドが提供されており、引数にプラス記号をエンコードする処理するインターセプターを渡せば対応できる。

インターセプターはリクエストに対する共通の処理(エンコードやロギング、監視、認証など)を実行したい場合に適している。

一方で、WebClientはこのメソッドが用意されていないため、似たような動作を行うfilterメソッドを変わりに利用する。

https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.Builder.html#:~:text=5.1-,filter,-WebClient.Builder filter

実装例

以下では、WebClientを返すメソッドに、プラス記号を%2BにエンコードするカスタムフィルターfilterPlusSignEncodingをアタッチした実装例を紹介する

import org.apache.commons.lang3.StringUtils;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;

  /**
   * WebClientの設定
   *
   * @return 設定が適用されたWebClientを返却
   */
  public WebClient webClient() {
    ConnectionProvider connectionProvider = ConnectionProvider.builder("tcp-client-pool")
      .maxConnections(poolMaxTotal)
      .build();

    HttpClient httpClient = HttpClient.create(connectionProvider)
      .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000)
      .doOnConnected(conn -> conn
      .responseTimeout(Duration.ofMillis(1000));

    return WebClient.builder()
+     .filter(filterPlusSignEncoding())
      .clientConnector(new ReactorClientHttpConnector(httpClient))
      .build();
  }

  /**
   * プラス記号をエンコードするインターセプター
   *
   * @return エンコードを施したurlを含むClientRequestを返却する
   */
  private ExchangeFilterFunction filterPlusSignEncoding() {
    return (clientRequest, nextFilter) -> {
      // プラス記号を%2Bにエンコードする
      String encodedUrl = StringUtils.replace(clientRequest.url().toString(), "+", "%2B");

      ClientRequest filteredRequest = ClientRequest.from(clientRequest)
        .url(URI.create(encodedUrl))
        .build();
      return nextFilter.exchange(filteredRequest);
    };
  }

filterPlusSignEncodingはインターフェースExchangeFilterFunctionを返すメソッドにしなければいけない。

また、作成したカスタムフィルターはWebClient.builder()を使用することで追加できる。

結果

実装したwebClientを使用することで、期待するURLでリクエストが可能になった。

http://localhost:8080/sample/?date=2022-08-01T11:22:33%2B09:00

まとめ

  • WebClientをそのまま利用するとプラス記号がスペースに変換されてしまった
  • RestTemplateでは、interceptorsを使えば対処できるがWebClientでは用意されていない
  • WebClientではfilterメソッドを利用する
  • プラス記号を%2Bへ変換するカスタムフィルターをアタッチすることで対処した

参考サイト

https://morioh.com/p/cf6cc98afdc4

株式会社ZOZO

Discussion