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("https://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("https://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("https://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レポジトリ]にあります。