webclient 和webflux(参考(webflux)) 这一系列,我们可以开好多节课程来讲解

什么是webclient,在spring5中,出现了reactive 响应式编程思想(参考响应式编程文章),并且为网络编程提供相关响应式编程的支持,如提供webflux(参考另外一个教程),他是spring 提供的异步非阻塞的响应式的网络框架,可以充分利用多cpu并行处理一些功能,虽然不能提高单个请求的响应能力,但是总体可以提高多核的服务器性能,提高系统吞吐量和伸缩性,特别适合于i/o密集型服务,webflux对应的是老式的sevlet的阻塞式服务容器。

而webclient提供的基于响应式的非阻塞的web请求客户端,对应的是老式的restTemplate这类,相对于老式的restTemplate,他不阻塞代码、异步执行。

他的执行流程是先创建个webclient.create()实例,之后调用get(),post()等调用方式,uri()指定路径,retrieve()用来发起请求并获得响应,bodyToMono(String.class)用来指定请求结果需要处理为String,并包装为Reactor的Mono对象,

简单实例如下:

WebClient webClient = WebClient.create();
Mono<String> mono = webClient.get().uri("https://www.baidu.com").retrieve().bodyToMono(String.class);
mono.subscribe(System.out::println);

以下我们讲解具体的实例

以下加webclient的pom

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><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><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>

一、webclient的使用

1、创建实例

WebClient webClient = WebClient.create();
// 如果是调用特定服务的API,可以在初始化webclient 时使用,baseUrl
WebClient webClient = WebClient.create("https://api.github.com");

或者用构造者模式构造

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

2、get请求

Mono<String> resp = WebClient.create()
      .method(HttpMethod.GET)
      .uri("http://www.baidu.com")
      .cookie("token","xxxx")
      .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
      .retrieve().bodyToMono(String.class);

3、post请求

 @Testpublic void testFormParam(){MultiValueMap<String, String> formData = new LinkedMultiValueMap();formData.add("name1","value1");formData.add("name2","value2");Mono<String> resp = WebClient.create().post().uri("http://www.w3school.com.cn/test/demo_form.asp").contentType(MediaType.APPLICATION_FORM_URLENCODED).body(BodyInserters.fromFormData(formData)).retrieve().bodyToMono(String.class);System.out.print("result:" + resp.block());}

可以使用BodyInserters类提供的各种工厂方法来构造BodyInserter对象并将其传递给body方法。BodyInserters类包含从Object,Publisher,Resource,FormData,MultipartData等创建BodyInserter的方法。

4、post json

static class Book {String name;String title;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}}@Testpublic void testPostJson(){Book book = new Book();book.setName("name");book.setTitle("this is title");Mono<String> resp = WebClient.create().post().uri("http://localhost:8080/demo/json").contentType(MediaType.APPLICATION_JSON_UTF8).body(Mono.just(book),Book.class).retrieve().bodyToMono(String.class);LOGGER.info("result:{}",resp.block());}

5、上传文件:

@Testpublic void testUploadFile(){HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.IMAGE_PNG);HttpEntity<ClassPathResource> entity = new HttpEntity<>(new ClassPathResource("parallel.png"), headers);MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();parts.add("file", entity);Mono<String> resp = WebClient.create().post().uri("http://localhost:8080/upload").contentType(MediaType.MULTIPART_FORM_DATA).body(BodyInserters.fromMultipartData(parts)).retrieve().bodyToMono(String.class);LOGGER.info("result:{}",resp.block());

6、失败处理

 @Testpublic void testFormParam4xx(){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.ResponseSpec responseSpec = webClient.method(HttpMethod.GET).uri("/user/repos?sort={sortField}&direction={sortDirection}","updated", "desc").retrieve();Mono<String> mono = responseSpec.onStatus(e -> e.is4xxClientError(),resp -> {log.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 -> {log.info("ERROR status:{},msg:{}",err.getRawStatusCode(),err.getResponseBodyAsString());throw new RuntimeException(err.getMessage());}).onErrorReturn("fallback");String result = mono.block();System.out.print(result);}
  1. 可以使用onStatus根据status code进行异常适配

  2. 可以使用doOnError异常适配

  3. 可以使用onErrorReturn返回默认值

7、指定url,并且替换api路径

在应用中使用WebClient时也许你要访问的URL都来自同一个应用,只是对应不同的URL地址,这个时候可以把公用的部分抽出来定义为baseUrl,然后在进行WebClient请求的时候只指定相对于baseUrl的URL部分即可。这样的好处是你的baseUrl需要变更的时候可以只要修改一处即可。下面的代码在创建WebClient时定义了baseUrl为http://localhost:8081,在发起Get请求时指定了URL为/user/1,而实际上访问的URL是http://localhost:8081/user/1

String baseUrl = "http://localhost:8081";
WebClient webClient = WebClient.create(baseUrl);
Mono<User> mono = webClient.get().uri("user/{id}", 1).retrieve().bodyToMono(User.class);

8、retrieve和exchange

retrieve方法是直接获取响应body,但是,如果需要响应的头信息、Cookie等,可以使用exchange方法,该方法可以访问整个ClientResponse。由于响应的得到是异步的,所以都可以调用 block 方法来阻塞当前程序,等待获得响应的结果。

以下代码是把获取写入的cookie

String baseUrl = "http://localhost:8081";
WebClient webClient = WebClient.create(baseUrl);MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("username", "u123");
map.add("password", "p123");Mono<ClientResponse> mono = webClient.post().uri("login").syncBody(map).exchange();
ClientResponse response = mono.block();
if (response.statusCode() == HttpStatus.OK) {Mono<Result> resultMono = response.bodyToMono(Result.class);resultMono.subscribe(result -> {if (result.isSuccess()) {ResponseCookie sidCookie = response.cookies().getFirst("sid");Flux<User> userFlux = webClient.get().uri("users").cookie(sidCookie.getName(), sidCookie.getValue()).retrieve().bodyToFlux(User.class);userFlux.subscribe(System.out::println);}});
}

9、filter

WebClient也提供了Filter,对应于org.springframework.web.reactive.function.client.ExchangeFilterFunction接口,其接口方法定义如下。Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next)在进行拦截时可以拦截request,也可以拦截response。

增加基本身份验证:

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

过滤response:

 @Testvoid filter() {Map<String, Object> uriVariables = new HashMap<>();uriVariables.put("p1", "var1");uriVariables.put("p2", 1);WebClient webClient = WebClient.builder().baseUrl("http://www.ifeng.com").filter(logResposneStatus()).defaultHeader(HttpHeaders.CONTENT_TYPE, "application/vnd.github.v3+json").build();Mono<String> resp1 = webClient.method(HttpMethod.GET).uri("/").cookie("token","xxxx").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).retrieve().bodyToMono(String.class);String re=  resp1.block();System.out.print("result:" +re);}private ExchangeFilterFunction logResposneStatus() {return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {log.info("Response Status {}", clientResponse.statusCode());return Mono.just(clientResponse);});}

使用过滤器记录日志:

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();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);};
}

注意:

将response body 转换为对象/集合

  • bodyToMono

    如果返回结果是一个Object,WebClient将接收到响应后把JSON字符串转换为对应的对象。

  • bodyToFlux

    如果响应的结果是一个集合,则不能继续使用bodyToMono(),应该改用bodyToFlux(),然后依次处理每一个元素。

二、webclient原理

webclient的整个实现技术基本采用reactor-netty实现,下章我讲述相关细节

参考:https://www.jb51.net/article/133384.htm

https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-client

webclient使用介绍相关推荐

  1. Spring5的WebClient使用详解

    1 前言 以前文章介绍了: RestTemplate:https://blog.csdn.net/zzhongcy/article/details/104674808 AsyncRestTemplat ...

  2. springboot-webFlux的webclient详细使用介绍,细节拉满

    文章目录 写在前面 一.配置-Configuration 1.基本用法 2.最大内存大小-MaxInMemorySize 3.配置Reactor Netty 4.配置使用Jetty 5.配置Apach ...

  3. SAP WebClient UI和business switch相关的逻辑介绍

    Do you know the meaning of these two checkboxes in F2 popup? Let's first learn some prerequisite kno ...

  4. SAP WebClient UI页面标签的决定逻辑介绍

    In this blog we have discussed the logic how the correct UI view configuration is chosen by framewor ...

  5. CRM WebClient UI的external service功能介绍

    要获取更多Jerry的原创文章,请关注公众号"汪子熙":

  6. Spring Cloud Alibaba基础教程:几种服务消费方式(RestTemplate、WebClient、Feign)

    热门:Spring Cloud Greenwich.RELEASE 正式发布!一个非常有看头的版本! 通过<Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现&g ...

  7. struct用法_精讲响应式webclient第1篇-响应式非阻塞IO与基础用法

    笔者在之前已经写了一系列的关于RestTemplate的文章,如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 精讲RestTemplate第2篇-多种底层HT ...

  8. webclient下载文件 带进度条

    private void button1_Click(object sender, EventArgs e){doDownload(textBox1.Text.Trim());}private Dat ...

  9. 一步一步学Silverlight 2系列(12):数据与通信之WebClient

    版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://terrylee.blog.51cto.com/342737/67249 ...

最新文章

  1. python数据可视化库 动态的_Python数据可视化:Pandas库,只要一行代码就能实现...
  2. 总结——LR学习总结
  3. 手写java数据库连接池,自定义实现数据库连接池,兼容springboot
  4. boost::contract模块实现check的测试程序
  5. 索引对mysql行锁和表锁影响
  6. 运行时动态调用子程序的例子
  7. wps vba模块压缩包_01_创建第一个VBA小程序:你好,世界
  8. 我的世界服务器怎么在计分板上面显示,我的世界计分板指令教程 计分板指令怎么使用...
  9. java 关闭守护线程_Java并发编程之线程生命周期、守护线程、优先级、关闭和join、sleep、yield、interrupt...
  10. 用Apache James搭建本地邮件服务器
  11. UWP 共享文件——接收者
  12. 海思3519A配置IP
  13. 下载并遍历 names.txt 文件,输出长度最长的回文人名。
  14. 20190922 On Java8 第二十一章 数组
  15. Cadence Orcad Capture 高亮整个网络的方法图文视频教程
  16. winhttp 访问https_「winhttp」C++用winhttp实现https访问服务器 - seo实验室
  17. 论文阅读:Aspect-based Sentiment Classification with Aspect-specific Graph Convolutional Networks
  18. 云栖大会day2总结 上午
  19. 自动驾驶什么时候才会凉凉,估计还要多久?
  20. 某侠图床源码 图片外链服务 带API

热门文章

  1. 记录一次“迅雷的不要脸行为”--取消浏览器响应下载
  2. MyEclipse注册码生成器
  3. 网络拓扑设计----hcip
  4. CAD2000坐标系转GIS84投影坐标系
  5. 计算机辅助技术基础知识点,计算机辅助设计技术基础知识.PPT
  6. CRC原理简析——史上最清新脱俗简单易懂的CRC解析
  7. Glass-theme cod mw2 for win7|Windows7主题下载
  8. 九、PHP知识核心 面向对象
  9. 2022年合束器市场前景分析及研究报告
  10. USB3.0和2.0有什么区别 --旧时光 oldtimeblog