有了 WebClient 还在用 RestTemplate?

发布于 2023-08-08 00:29:58 字数 7473 浏览 35 评论 0

Spring 官方推荐使用 WebClient 替代 RestTemplate 完成 HTTP 调用。因为 WebClient 是基于 Reactor 实现的,所以既可以支持阻塞调用也可以支持非阻塞调用,在高并发的场景下资源利用率更高。这也是官方推荐使用的重要原因之一。

如果大家不了解响应式编程,强烈建议可以先看一下我 这篇文章 ,这样对本篇文章中的样例代码也会有些帮助。

如果在工程中想要用 WebClient ,在 Pom 文件中加入如下依赖

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

如果使用的是 Gradle ,则加入如下依赖

dependencies {
compile 'org.springframework.boot:spring-boot-starter-webflux'
}​

初始化 WebClient

  1. 直接初始化,不加任何参数。

    WebClient client = WebClient.create();
  2. 初始化时,提供一个默认的调用地址。

    WebClient client = WebClient.create("http://localhost:8080");
  3. 自定义参数初始化。

    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();
  4. 修改默认的超时时间。

    //通过 HttpClient 设置超时时间
    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();

发起 Get 请求

  1. 以阻塞的方式获取 0 或者 1 个返回结果,用 Mono 表示

    // 通过 builder 的方式初始化
    WebClientWebClient webClient = WebClient.builder()
    // 配置头部信息 .defaultHeader(HttpHeaders.ACCEPT_CHARSET, "UTF-8") //定义过滤器 .filter(ExchangeFilterFunctions.basicAuthentication("user","password")) .filter((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); }) .build(); //发起 GET 请求 Mono<String> resp = webClient.get() .uri("https://localhost:8080") //获取结果 .retrieve() //将结果转化为指定类型 .bodyToMono(String.class); //以阻塞的方式将结果打印出来 logger.info("result:{}",resp.block());
  2. 以阻塞方式获取多个返回结果,用 Flux 表示

    Flux<Book> bookFlux = WebClient.create()
                              .method(HttpMethod.GET)
                             .uri("http://localhost:8080/books")
                             .retrieve()
                             .bodyToFlux(Book.class);
     //通过阻塞的方式获取响应结果
     List<Book> books = bookFlux.collectList().block();
     //通过非阻塞的方式获取响应结果
     bookFlux.subscribe(book ->{System.out.print(book.getName());});
  3. 通过非阻塞方式获取响应结果

    Flux<Book> bookFlux = WebClient.create()
                              .method(HttpMethod.GET)
                             .uri("http://localhost:8080/books")
                             .retrieve()
                             .bodyToFlux(Book.class);
     //通过非阻塞的方式获取响应结果
     bookFlux.subscribe(book ->{System.out.print(book.getName());});
  4. 通过占位符传参

    Mono<String> mono = WebClient.create()
                        .method(HttpMethod.POST)
                      .uri("http://localhost:8080/book/   {id}/{name}", "1", "java")
                      .retrieve()
                      .bodyToMono(String.class);
                      String result = mono.block();

除了占位符传参,还可以通过 map 形式传参等等,这里不一一举例介绍了。

发起 POST 请求

  1. 发起 POST 请求,提交 Form 表单

    MultiValueMap<String, String> formData = new 
    LinkedMultiValueMap<>();
    formData.add("name1","value1");
    formData.add("name2","value2");
    Mono<String> resp = WebClient.create().post()
                        .uri("http://localhost:8080/submit")
    .contentType(MediaType.APPLICATION_FORM_URLENCODED) .body(BodyInserters.fromFormData(formData))
    .retrieve().bodyToMono(String.class); logger.info("result:{}",resp.block());
  2. 使用 Raw Json 的方式发起 POST 请求。

    Mono<String> resp = WebClient.create().post()
                        .uri("http://localhost:8080/book/json")
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue("{\n" +
    " \"name\" : \"java\",\n" +
    " \"price\" : \"32.5\" \n" +
    " }"))
    .retrieve().bodyToMono(String.class); logger.info("result:{}",resp.block());

错误和异常处理

WebClient 可以更优雅的处理错误和异常。

// 创建 WebClient
WebClient webClient = WebClient.builder()
  .baseUrl("http://localhost:8080")
  .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/json")
  .defaultHeader(HttpHeaders.ACCEPT_CHARSET, "UTF-8")
  .build();
// 发起 Get 请求
WebClient.ResponseSpec responseSpec = webClient.method(HttpMethod.GET)
  .uri("/book/remark/{id}", "1")
  .retrieve();
// 根据状态码进行响应
Mono<String> mono = responseSpec
  .onStatus(e -> e.is4xxClientError(),resp -> {
      logger.error("error:{},msg:{}",resp.statusCode().value(),resp.statusCode().getReasonPhrase());
      return Mono.error(new RuntimeException(resp.statusCode().value() + " : " + resp.statusCode().getReasonPhrase()));
  })
  .bodyToMono(String.class)
  .doOnError(WebClientResponseException.class, err -> {
      logger.info("ERROR status:{},msg:{}",err.getRawStatusCode(),err.getResponseBodyAsString());
      throw new RuntimeException(err.getMessage());
  })
  .onErrorReturn("fallback");
String result = mono.block();
logger.info("result:{}",result);

总结

以上是一些 WebClient 使用的小 Demo ,希望对那些想了解 WebClient 的同学有一些帮助。如果想深入了解,建议还是多看看官方文档。

推荐阅读

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

望笑

暂无简介

0 文章
0 评论
21 人气
更多

推荐作者

qq_E2Iff7

文章 0 评论 0

Archangel

文章 0 评论 0

freedog

文章 0 评论 0

Hunk

文章 0 评论 0

18819270189

文章 0 评论 0

wenkai

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文