Spring 5 WebClient和WebTestClient使用教程

编程教程 > Java > Spring (4113) 2024-11-26 14:39:04

1.引言

Spring开发人员,您是否曾经觉得需要一个易于使用且高效的流畅功能样式 API 的异步/非阻塞 HTTP客户端?

如果是,那么我欢迎您阅读关于WebClient的文章,WebClient是Spring 5中引入的新的被动HTTP客户端。

 

2.如何使用WebClient

WebClient是Spring 5的反应性Web框架Spring WebFlux的一部分。要使用WebClient,您需要将spring-webflux模块包含在您的项目中。

在现有的Spring Boot项目中添加依赖项

如果您有一个现有的Spring Boot项目,则可以spring-webflux通过在该pom.xml文件中添加以下依赖项来添加该模块-

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

请注意,您需要Spring Boot 2.xx版本才能使用Spring WebFlux模块。

从Scratch创建一个新项目

如果您从头开始创建项目,那么您可以使用Spring Initializr网站的spring-webflux模块生成初始项目-

  1. 转到http://start.spring.io
  2. 选择弹簧引导版本2.xx的
  3. 在依赖项部分添加反应性Web依赖项。
  4. 如果需要,请更改工件的详细信息,然后单击生成工程下载项目。

3.使用WebClient消费远程API

让我们做一些有趣的事情,并使用WebClient来使用Real World API。

在本文中,我们将使用WebClient来使用Github的API。我们将使用WebClient在用户的Github存储库上执行CRUD操作。

创建WebClient的一个实例

1.使用该create()方法创建WebClient

您可以使用create()工厂方法创建WebClient的实例-

WebClient webClient = WebClient.create();

如果您只使用特定服务的API,那么您可以使用该服务的baseUrl来初始化WebClient

WebClient webClient = WebClient.create("https://api.github.com");

2.使用WebClient构建器创建WebClient

WebClient还附带了一个构建器,它为您提供了一些自定义选项,包括过滤器,默认标题,cookie,客户端连接器等 -

WebClient webClient = WebClient.builder()
        .baseUrl("https://api.github.com")
        .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/vnd.github.v3+json")
        .defaultHeader(HttpHeaders.USER_AGENT, "Spring 5 WebClient")
        .build();

使用WebClient发出请求并检索响应

以下是如何使用WebClient GETGithub的List Repositories API发出请求-

public Flux<GithubRepo> listGithubRepositories(String username, String token) {
     return webClient.get()
            .uri("/user/repos")
            .header("Authorization", "Basic " + Base64Utils
                    .encodeToString((username + ":" + token).getBytes(UTF_8)))
            .retrieve()
            .bodyToFlux(GithubRepo.class);
}

了解API调用的简单性和简洁性!

假设我们有一个名为类GithubRepo,确认到GitHub的API响应,上面的函数会返回一个FluxGithubRepo对象。

请注意,我使用Github的基本认证机制来调用API。它需要您的github用户名和个人访问令牌,您可以从https://github.com/settings/tokens中生成该令牌。

使用exchange()方法来检索响应

retrieve()方法是获取响应主体的最简单方法。但是,如果您希望对响应拥有更多的控制权,那么您可以使用可exchange()访问整个ClientResponse标题和正文的方法 -

public Flux<GithubRepo> listGithubRepositories(String username, String token) {
     return webClient.get()
            .uri("/user/repos")
            .header("Authorization", "Basic " + Base64Utils
                    .encodeToString((username + ":" + token).getBytes(UTF_8)))
            .exchange()
            .flatMapMany(clientResponse -> clientResponse.bodyToFlux(GithubRepo.class));
}

在请求URI中使用参数

您可以在请求URI中使用参数,并在uri()函数中分别传递它们的值。所有参数都被花括号包围。在提出请求之前,这些参数将被WebClient自动替换 -

public Flux<GithubRepo> listGithubRepositories(String username, String token) {
     return webClient.get()
            .uri("/user/repos?sort={sortField}&direction={sortDirection}", 
                     "updated", "desc")
            .header("Authorization", "Basic " + Base64Utils
                    .encodeToString((username + ":" + token).getBytes(UTF_8)))
            .retrieve()
            .bodyToFlux(GithubRepo.class);
}

使用URIBuilder构造请求URI

您也可以使用UriBuilder类似的方法获取对请求URI的完全程序控制,

public Flux<GithubRepo> listGithubRepositories(String username, String token) {
     return webClient.get()
            .uri(uriBuilder -> uriBuilder.path("/user/repos")
                    .queryParam("sort", "updated")
                    .queryParam("direction", "desc")
                    .build())
            .header("Authorization", "Basic " + Base64Utils
                    .encodeToString((username + ":" + token).getBytes(UTF_8)))
            .retrieve()
            .bodyToFlux(GithubRepo.class);
}

在WebClient请求中传递Request Body

如果你有一个Mono或一个形式的请求体Flux,那么你可以直接将它传递给body()WebClient中的方法,否则你可以从一个对象中创建一个单声道/通量并像这样传递 -

public Mono<GithubRepo> createGithubRepository(String username, String token, 
    RepoRequest createRepoRequest) {
    return webClient.post()
            .uri("/user/repos")
            .body(Mono.just(createRepoRequest), RepoRequest.class)
            .header("Authorization", "Basic " + Base64Utils
                    .encodeToString((username + ":" + token).getBytes(UTF_8)))
            .retrieve()
            .bodyToMono(GithubRepo.class);
}
如果您具有实际值而不是PublisherFluxMono),则可以使用syncBody()快捷方式传递请求正文 -
public Mono<GithubRepo> createGithubRepository(String username, String token, 
    RepoRequest createRepoRequest) {
    return webClient.post()
            .uri("/user/repos")
            .syncBody(createRepoRequest)
            .header("Authorization", "Basic " + Base64Utils
                    .encodeToString((username + ":" + token).getBytes(UTF_8)))
            .retrieve()
            .bodyToMono(GithubRepo.class);
}
最后,你可以使用BodyInserters类提供的各种工厂方法来构造一个BodyInserter对象并将其传递给该body()方法。本BodyInserters类包含的方法来创建一个BodyInserterObjectPublisherResourceFormDataMultipartData等-
public Mono<GithubRepo> createGithubRepository(String username, String token, 
    RepoRequest createRepoRequest) {
    return webClient.post()
            .uri("/user/repos")
            .body(BodyInserters.fromObject(createRepoRequest))
            .header("Authorization", "Basic " + Base64Utils
                    .encodeToString((username + ":" + token).getBytes(UTF_8)))
            .retrieve()
            .bodyToMono(GithubRepo.class);
}

添加过滤器功能

WebClient支持使用ExchangeFilterFunction。您可以使用过滤器函数以任何方式拦截和修改请求。例如,您可以使用过滤器函数为Authorization每个请求添加一个标头,或记录每个请求的详细信息。

ExchangeFilterFunction需要两个参数 -

  1. ClientRequest
  2. ExchangeFilterFunction过滤器链中的下一个。

它可以修改ClientRequest并调用ExchangeFilterFucntion过滤器链中的下一个来继续下一个过滤器或ClientRequest直接返回修改以阻止过滤器链。

1.使用过滤器功能添加基本认证

在上面的所有示例中,我们都包含一个Authorization用于使用Github API进行基本身份验证的标头。由于这是所有请求共有的内容,因此您可以在创建过滤器函数时将此逻辑添加到过滤器函数中WebClient

ExchaneFilterFunctionsAPI已经为基本认证提供了一个过滤器。你可以像这样使用它 -

WebClient webClient = WebClient.builder()
        .baseUrl(GITHUB_API_BASE_URL)
        .defaultHeader(HttpHeaders.CONTENT_TYPE, GITHUB_V3_MIME_TYPE)
        .filter(ExchangeFilterFunctions
                .basicAuthentication(username, token))
        .build();

现在,您不需要Authorization在每个请求中添加标题。过滤器函数将拦截每个WebClient请求添加此标头。

2.使用过滤器功能记录所有请求

我们来看一个习惯的例子ExchangeFilterFunction。我们将编写一个过滤器函数来拦截并记录每个请求 -

WebClient webClient = WebClient.builder()
        .baseUrl(GITHUB_API_BASE_URL)
        .defaultHeader(HttpHeaders.CONTENT_TYPE, GITHUB_V3_MIME_TYPE)
        .filter(ExchangeFilterFunctions
                .basicAuthentication(username, token))
        .filter(logRequest())
        .build();
这里是logRequest()过滤器功能的实现-
private ExchangeFilterFunction logRequest() {
    return (clientRequest, next) -> {
        logger.info("Request: {} {}", clientRequest.method(), clientRequest.url());
        clientRequest.headers()
                .forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value)));
        return next.exchange(clientRequest);
    };
}

3.使用ofRequestProcessor()和ofResponseProcessor()工厂方法来创建过滤器

ExchangeFilterFunction API提供两个名为工厂方法ofRequestProcessor()ofResponseProcessor()用于创建分别截获该请求和响应滤波器的功能。

logRequest()我们在前一节中创建的过滤器函数可以使用ofRequestProcessor()这种工厂方法创建-

private ExchangeFilterFunction logRequest() {
    ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
        logger.info("Request: {} {}", clientRequest.method(), clientRequest.url());
        clientRequest.headers()
                .forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value)));
        return Mono.just(clientRequest);
    });
}        
如果您想拦截WebClient响应,则可以使用该ofResponseProcessor()方法创建像这样的过滤器功能 -
private ExchangeFilterFunction logResposneStatus() {
    return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
        logger.info("Response Status {}", clientResponse.statusCode());
        return Mono.just(clientResponse);
    });
}

处理WebClient错误

只要接收到状态码为4xx或5xx的响应retrieve(),WebClient中的方法WebClientResponseException就会抛出一个。

您可以使用onStatus()像这样的方法来自定义,
 

public Flux<GithubRepo> listGithubRepositories() {
     return webClient.get()
            .uri("/user/repos?sort={sortField}&direction={sortDirection}", 
                     "updated", "desc")
            .retrieve()
            .onStatus(HttpStatus::is4xxClientError, clientResponse ->
                Mono.error(new MyCustomClientException())
            )
            .onStatus(HttpStatus::is5xxServerError, clientResponse ->
                Mono.error(new MyCustomServerException())
            )
            .bodyToFlux(GithubRepo.class);

}

请注意,与retrieve()方法不同,该exchange()方法在4xx或5xx响应的情况下不会引发异常。您需要自己检查状态代码,并以您想要的方式处理它们。

使用@ExceptionHandler控制器内部的WebClientResponseExceptions处理

您可以@ExceptionHandler在控制器内部使用这种方式来处理WebClientResponseException并返回适当的响应给客户端 -

@ExceptionHandler(WebClientResponseException.class)
public ResponseEntity<String> handleWebClientResponseException(WebClientResponseException ex) {
    logger.error("Error from WebClient - Status {}, Body {}", ex.getRawStatusCode(), ex.getResponseBodyAsString(), ex);
    return ResponseEntity.status(ex.getRawStatusCode()).body(ex.getResponseBodyAsString());
}

使用Spring 5 WebTestClient测试Rest API

WebTestClient包含类似于WebClient的请求方法。另外,它还包含检查响应状态,标题和正文的方法。您也可以像AssertJ使用WebTestClient 一样使用断言库。

查看以下示例以了解如何使用WebTestClient执行其他API测试 -

提示:项目源码下载spring-webclient-webtestclient-demo-master.zip


评论
User Image
提示:请评论与当前内容相关的回复,广告、推广或无关内容将被删除。

相关文章
1.引言Spring开发人员,您是否曾经觉得需要一个易于使用且高效的流畅功能样式 API 的异步/非阻塞 HTTP客户端?如果是,那么我欢迎您阅读关于WebClient的文章,WebClient...
spring boot webflux client实战,webclient是spring webflux的一个小组件。对于Java的http通讯来说,webclient是非常简单易用的。
Spring框架5.0,spring mvc 5.0入门教程。DispatcherServlet的详细讲解配置以及spring mvc5.0的helloword程序
spring boot 2.0 security 5.0 整合,实现自定义表单登录。spring boot 2.0框架使用。
java编程中spring框架5.0介绍说明/概述,spring5,spring框架,java编程
spring boot 1.5整合redis实现spring的缓存框架,spring boot,redis
在这个Spring5教程中,学习创建Spring 5 MVC Web应用程序,处理表单提交,集成hibernate连接到后端数据库,以及添加用于输入表单字段验证的hibernate验证器。
Java编程中使用spring boot1.5.2框架整合spring data mongodb10.1,来使用mongodb数据库
Spring 5 入门实战Say Hello,本博客主要讲解spring 5最基础的容器入门实战。
Spring WebFlux,spring框架5.0将会新增的web增强框架,这里主要讲述什么是Spring WebFlux以及Spring WebFlux的新功能,Spring WebFlux...
引言    通过之前spring boot mybatis 整合的讲解: spring boot mybaties整合  (spring boot mybaties 整合 基于Java注解方式写...
Spring Boot 2.1 新特性,已升级Spring 版本为5.1,支持servlet 4.0,支持Tomcat 9.0等等
spring boot入门,spring boot是一个崭新的spring框架分支项目,本文讲解其属性配置相关
Spring Boot 2.0,Spring框架的Spring Boot 中的Spring Boot Actuator变化讲解。并且了解如何在Spring Boot 2.0中使用Actuator...
前言    本教程主要讲解spring boot如何整合 spring data elasticsearch 实现elasticsearch检索引擎的整合使用