Spring 5 Webクライアント

1概要

この記事では、Spring 5で導入されているリアクティブWebクライアントである WebClient を紹介します。

私たちは WebTestClient も見てみるつもりです - これはテストで使われるように設計された WebClient です。

2 WebClient とは何ですか?

簡単に言えば、 WebClient はWeb要求を実行するための主要なエントリポイントを表すインタフェースです。

これはSpring Web Reactiveモジュールの一部として作成されており、これらのシナリオでは従来の RestTemplate に代わるものとなるでしょう。新しいクライアントは、HTTP/1.1プロトコル上で機能する、反応性のある非ブロッキングソリューションです。

最後に、このインターフェースには DefaultWebClient クラスという単一の実装があり、それを処理します。

3依存関係

Spring Bootアプリケーションを使用しているので、https://search.maven.org/classic/#search%7Cga%7C1%7Ca%3A%22spring-boot-starter-webflux%22%20AND%20g%3Aが必要です。 %22org.springframework.boot%22[ spring-boot-starter-webflux ]依存関係、およびhttp://repo1.maven.org/maven2/org/projectreactor/reactor-spring/[Reactorプロジェクト]。

3.1. Mavenを使った構築

pom.xml ファイルに次の依存関係を追加しましょう。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
    <groupId>org.projectreactor</groupId>
    <artifactId>reactor-spring</artifactId>
    <version>1.0.1.RELEASE</version>
</dependency>

3.2. Gradleを使った構築

Gradleでは、 build.gradle ファイルに次のエントリを追加する必要があります。

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-webflux'
    compile 'org.projectreactor:reactor-spring:1.0.1.RELEASE'
}

4 WebClient を使った作業

クライアントと正しく連携するには、以下の方法を知っておく必要があります。

  • インスタンスを作成する

  • リクエストする

  • 応答を処理する

4.1. WebClient インスタンスを作成する

から選択する3つのオプションがあります。 1つ目は、デフォルト設定で WebClient オブジェクトを作成することです。

WebClient client1 = WebClient.create();

2番目の方法では、指定されたベースURIを使って WebClient インスタンスを起動することができます。

WebClient client2 = WebClient.create("http://localhost:8080");

最後の方法(そして最も高度な方法)は DefaultWebClientBuilder クラスを使ってクライアントを構築することです。

WebClient client3 = WebClient
  .builder()
    .baseUrl("http://localhost:8080")
    .defaultCookie("cookieKey", "cookieValue")
    .defaultHeader(HttpHeaders.CONTENT__TYPE, MediaType.APPLICATION__JSON__VALUE)
    .defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080"))
  .build();

4.2. リクエストを準備する

まず、 method(HttpMethodメソッド) を呼び出すか、 get post delete などのショートカットメソッドを呼び出して、要求のHTTPメソッドを指定する必要があります。

WebClient.UriSpec<WebClient.RequestBodySpec> request1 = client3.method(HttpMethod.POST);
WebClient.UriSpec<WebClient.RequestBodySpec> request2 = client3.post();

次の動きはURLを提供することです。それを String または java.net.URL インスタンスとして uri APIに渡すことができます。

WebClient.RequestBodySpec uri1 = client3
  .method(HttpMethod.POST)
  .uri("/resource");

WebClient.RequestBodySpec uri2 = client3
  .post()
  .uri(URI.create("/resource"));

続けて、必要に応じて、リクエストボディ、コンテンツタイプ、長さ、Cookie、またはヘッダーを設定できます。

例えば、リクエストボディを設定したい場合 - 2つの利用可能な方法があります - それを BodyInserter で満たすか、この仕事を Publisher に委任する:

WebClient.RequestHeadersSpec requestSpec1 = WebClient
  .create()
  .method(HttpMethod.POST)
  .uri("/resource")
  .body(BodyInserters.fromPublisher(Mono.just("data")), String.class);

WebClient.RequestHeadersSpec<?> requestSpec2 = WebClient
  .create("http://localhost:8080")
  .post()
  .uri(URI.create("/resource"))
  .body(BodyInserters.fromObject("data"));
  • BodyInserter は、 ReactiveHttpOutputMessage bodyに挿入時に使用される指定された出力メッセージとコンテキストを入力する役割を果たします。

2番目の方法は body メソッドです。これは元の body(BodyInserter inserter) メソッドのショートカットです。

BodyInserterを埋めるこのプロセスを軽減するために、 BodyInserters__クラスがあり、これは便利なユーティリティーメソッドの集まりです。

BodyInserter<Publisher<String>, ReactiveHttpOutputMessage> inserter1 = BodyInserters
  .fromPublisher(Subscriber::onComplete, String.class);

MultiValueMap でも可能です。

LinkedMultiValueMap map = new LinkedMultiValueMap();

map.add("key1", "value1");
map.add("key2", "value2");

BodyInserter<MultiValueMap, ClientHttpRequest> inserter2
 = BodyInserters.fromMultipartData(map);

あるいは単一のオブジェクトを使用することによって:

BodyInserter<Object, ReactiveHttpOutputMessage> inserter3
 = BodyInserters.fromObject(new Object());

本文を設定したら、ヘッダー、Cookie、使用可能なメディアの種類を設定できます。クライアントをインスタンス化するときに設定された値に値が追加されます。

また、__「If-None-Match」、「If-Modified-Since」、「Accept」、「Accept-Charset」など、最も一般的に使用されるヘッダーの追加サポート

これらの値を使用する方法の例を次に示します。

WebClient.ResponseSpec response1 = uri1
  .body(inserter3)
    .header(HttpHeaders.CONTENT__TYPE, MediaType.APPLICATION__JSON__VALUE)
    .accept(MediaType.APPLICATION__JSON, MediaType.APPLICATION__XML)
    .acceptCharset(Charset.forName("UTF-8"))
    .ifNoneMatch("** ")
    .ifModifiedSince(ZonedDateTime.now())
  .retrieve();

4.3. 回答を得る

最後の段階は、要求を送信して応答を受信することです。これは、 exchange メソッドまたは retrieve メソッドのどちらでも実行できます。

それらは戻り型が異なります。 exchange メソッドはステータス、ヘッダとともに ClientResponse を提供し、 retrieve メソッドはボディを直接取得するための最短パスです。

String response2 = request1.exchange()
  .block()
  .bodyToMono(String.class)
  .block();
String response3 = request2
  .retrieve()
  .bodyToMono(String.class)
  .block();

bodyToMono メソッドに注意してください。ステータスコードが 4xx (クライアントエラー)または 5xx (サーバーエラー)の場​​合は、 WebClientException がスローされます。応答とともに送信された実際のデータをサブスクライブして取得するために、 __Mon sで block__メソッドを使用しました。

5 WebTestClient を使った作業

WebTestClient はWebFluxサーバーエンドポイントをテストするための主要なエントリポイントです。これは WebClient と非常によく似たAPIを持ち、主にテストコンテキストの提供に焦点を合わせて、ほとんどの作業を内部の WebClient インスタンスに委任しています。 DefaultWebTestClient クラスは単一のインターフェイス実装です。

テスト用のクライアントは、実サーバーにバインドすることも、特定のコントローラーや機能を処理することもできます。実行中のサーバーへの実際のリクエストでエンドツーエンドの統合テストを完了するには、 bindToServer メソッドを使用できます。

WebTestClient testClient = WebTestClient
  .bindToServer()
  .baseUrl("http://localhost:8080")
  .build();

特定の RouterFunction bindToRouterFunction メソッドに渡すことでテストできます。

RouterFunction function = RouterFunctions.route(
  RequestPredicates.GET("/resource"),
  request -> ServerResponse.ok().build()
);

WebTestClient
  .bindToRouterFunction(function)
  .build().get().uri("/resource")
  .exchange()
  .expectStatus().isOk()
  .expectBody().isEmpty();

WebHandler インスタンスをとる bindToWebHandler メソッドを使用しても同じ動作を実現できます。

WebHandler handler = exchange -> Mono.empty();
WebTestClient.bindToWebHandler(handler).build();

bindToApplicationContext メソッドを使用していると、さらに興味深い状況が発生します。それは ApplicationContext を取り、コントローラBeanのコンテキストと @ EnableWebFlux 設定を分析します。

ApplicationContext のインスタンスを挿入すると、単純なコードスニペットは次のようになります。

@Autowired
private ApplicationContext context;

WebTestClient testClient = WebTestClient.bindToApplicationContext(context)
  .build();

もっと短いアプローチは bindToController メソッドでテストしたいコントローラーの配列を提供することです。 Controller クラスを取得し、それを必要なクラスにインジェクトしたと仮定すると、次のように書くことができます。

@Autowired
private Controller controller;

WebTestClient testClient = WebTestClient.bindToController(controller).build();

WebTestClient オブジェクトを構築した後は、チェーン内の以降のすべての操作は、 exchangeStメソッドのような WebTestClient.ResponseSpec インターフェースを提供する exchange メソッドまでの WebClient に似たものになります。 expectBody expectHeader__:

WebTestClient
  .bindToServer()
    .baseUrl("http://localhost:8080")
    .build()
    .post()
    .uri("/resource")
  .exchange()
    .expectStatus().isCreated()
    .expectHeader().valueEquals("Content-Type", "application/json")
    .expectBody().isEmpty();

6. 結論

このチュートリアルでは、クライアント側でリクエストを行うための新しい強化されたSpringメカニズム、つまり WebClient クラスについて説明しました。

また、リクエスト処理を通じてすべての利点を提供しています。

この記事に記載されているすべてのコードスニペットは、https://github.com/eugenp/tutorials/tree/master/spring-5-reactive[私たちのGitHubレポジトリ]にあります。