WebCLient 概述
WebClient 是一个接口,表示执行 Web 请求的主要入口点。
是 Spring Web Reactive 模块的一部分,用于取代经典的 RestTemplate。此外,这个新的客户端是一个基于 HTTP/1.1 协议的响应式、非阻塞解决方案。
该接口只有一个实现,即我们将要使用的 DefaultWebClient 类
使用
- 引入依赖
spring-boot-starter-webflux
- 使用 maven 构建
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
|
使用
创建实例
- 使用 WebClient 对象
1
| WebClient client = WebClient.create();
|
- 使用给定的 url 初始化
1
| WebClient client = WebClient.create("http://localhost:8080");
|
- *使用 DefaultWebClientBuilder 类来构建
推荐使用:可以自定义 WebClient 实例
1 2 3 4 5 6
| WebClient client = 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();
|
指定超出时间
通常情况下,默认的 30 秒 HTTP 超时时间太慢,无法满足需要,要自定义这种行为,可以创建一个 HttpClient 实例,并配置 WebClient 使用它。
- 通过 ChannelOption.CONNECT_TIMEOUT_MILLIS 选项设置连接超时。
- 分别使用 ReadTimeoutHandler 和写 WriteTimeoutHandler 设置读、写超时。
- 使用 responseTimeout 指令配置响应超时。
配置:在 HttpClient 实例中指定
1 2 3 4 5 6 7 8 9 10
| HttpClient httpClient = HttpClient.create() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) .responseTimeout(Duration.ofMillis(5000)) .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS)) .addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS)));
WebClient client = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .build();
|
这是 Mono / Flux Publisher 的超时。
定义方法
调用 method(HttpMethod method) 来指定请求的 HTTP 方法:
1 2 3 4
| UriSpec<RequestBodySpec> uriSpec = client.method(HttpMethod.POST);
UriSpec<RequestBodySpec> uriSpec = client.post();
|
定义 url
实现:
1 2 3 4 5 6 7
| RequestBodySpec bodySpec = uriSpec.uri("/resource");
```java RequestBodySpec bodySpec = uriSpec.uri( uriBuilder -> uriBuilder.pathSegment("/resource").build());
|
定义请求体
1 2 3 4 5 6 7 8 9 10 11 12 13
| RequestHeadersSpec<?> headersSpec = bodySpec.bodyValue("data");
RequestHeadersSpec headersSpec = bodySpec.body( BodyInserters.fromPublisher(Mono.just("data")), String.class);
LinkedMultiValueMap map = new LinkedMultiValueMap(); map.add("key1", "value1"); map.add("key2", "value2"); RequestHeadersSpec<?> headersSpec = bodySpec.body( BodyInserters.fromMultipartData(map));
|
示例:
1 2 3 4 5 6 7
| ResponseSpec responseSpec = headersSpec.header( HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML) .acceptCharset(StandardCharsets.UTF_8) .ifNoneMatch("*") .ifModifiedSince(ZonedDateTime.now()) .retrieve();
|
获取响应
方式一:
1 2 3 4 5 6 7 8 9 10
| Mono<String> response = headersSpec.exchangeToMono(response -> { if (response.statusCode().equals(HttpStatus.OK)) { return response.bodyToMono(String.class); } else if (response.statusCode().is4xxClientError()) { return Mono.just("Error response"); } else { return response.createException() .flatMap(Mono::error); } });
|
方式二:
1 2
| Mono<String> response = headersSpec.retrieve() .bodyToMono(String.class);
|
WebTestClient
WebTestClient 是测试 WebFlux 服务器端点的主要入口。它的 API 与 WebClient 非常相似,它将大部分工作委托给内部 WebClient 实例,主要侧重于提供测试上下文。DefaultWebTestClient 类是 WebTestClient 的唯一接口实现。
绑定到服务器
1 2 3 4
| WebTestClient testClient = WebTestClient .bindToServer() .baseUrl("http://localhost:8080") .build();
|
绑定到路由
1 2 3 4 5 6 7 8 9 10 11
| RouterFunction function = RouterFunctions.route( RequestPredicates.GET("/resource"), request -> ServerResponse.ok().build() );
WebTestClient .bindToRouterFunction(function) .build().get().uri("/resource") .exchange() .expectStatus().isOk() .expectBody().isEmpty();
|
绑定到 WebHandler
绑定到 ApplicationContext
绑定到 Controller
1 2 3 4
| @Autowired private Controller controller;
WebTestClient testClient = WebTestClient.bindToController(controller).build();
|
发起请求
构建 WebTestClient 对象后,所有后续操作都与 WebClient 类似,直到 exchange 方法(获取响应的一种方法),该方法提供了 WebTestClient.ResponseSpec 接口,可使用诸如 expectStatus、expectBody 和 expectHeader 等有用的方法进行操作:
1 2 3 4 5 6 7 8 9 10
| WebTestClient .bindToServer() .baseUrl("http://localhost:8080") .build() .post() .uri("/resource") .exchange() .expectStatus().isCreated() .expectHeader().valueEquals("Content-Type", "application/json") .expectBody().jsonPath("field").isEqualTo("value");
|