webclient使用介绍
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);}
可以使用onStatus根据status code进行异常适配
可以使用doOnError异常适配
可以使用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使用介绍相关推荐
- Spring5的WebClient使用详解
1 前言 以前文章介绍了: RestTemplate:https://blog.csdn.net/zzhongcy/article/details/104674808 AsyncRestTemplat ...
- springboot-webFlux的webclient详细使用介绍,细节拉满
文章目录 写在前面 一.配置-Configuration 1.基本用法 2.最大内存大小-MaxInMemorySize 3.配置Reactor Netty 4.配置使用Jetty 5.配置Apach ...
- SAP WebClient UI和business switch相关的逻辑介绍
Do you know the meaning of these two checkboxes in F2 popup? Let's first learn some prerequisite kno ...
- SAP WebClient UI页面标签的决定逻辑介绍
In this blog we have discussed the logic how the correct UI view configuration is chosen by framewor ...
- CRM WebClient UI的external service功能介绍
要获取更多Jerry的原创文章,请关注公众号"汪子熙":
- Spring Cloud Alibaba基础教程:几种服务消费方式(RestTemplate、WebClient、Feign)
热门:Spring Cloud Greenwich.RELEASE 正式发布!一个非常有看头的版本! 通过<Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现&g ...
- struct用法_精讲响应式webclient第1篇-响应式非阻塞IO与基础用法
笔者在之前已经写了一系列的关于RestTemplate的文章,如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 精讲RestTemplate第2篇-多种底层HT ...
- webclient下载文件 带进度条
private void button1_Click(object sender, EventArgs e){doDownload(textBox1.Text.Trim());}private Dat ...
- 一步一步学Silverlight 2系列(12):数据与通信之WebClient
版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://terrylee.blog.51cto.com/342737/67249 ...
最新文章
- python数据可视化库 动态的_Python数据可视化:Pandas库,只要一行代码就能实现...
- 总结——LR学习总结
- 手写java数据库连接池,自定义实现数据库连接池,兼容springboot
- boost::contract模块实现check的测试程序
- 索引对mysql行锁和表锁影响
- 运行时动态调用子程序的例子
- wps vba模块压缩包_01_创建第一个VBA小程序:你好,世界
- 我的世界服务器怎么在计分板上面显示,我的世界计分板指令教程 计分板指令怎么使用...
- java 关闭守护线程_Java并发编程之线程生命周期、守护线程、优先级、关闭和join、sleep、yield、interrupt...
- 用Apache James搭建本地邮件服务器
- UWP 共享文件——接收者
- 海思3519A配置IP
- 下载并遍历 names.txt 文件,输出长度最长的回文人名。
- 20190922 On Java8 第二十一章 数组
- Cadence Orcad Capture 高亮整个网络的方法图文视频教程
- winhttp 访问https_「winhttp」C++用winhttp实现https访问服务器 - seo实验室
- 论文阅读:Aspect-based Sentiment Classification with Aspect-specific Graph Convolutional Networks
- 云栖大会day2总结 上午
- 自动驾驶什么时候才会凉凉,估计还要多久?
- 某侠图床源码 图片外链服务 带API
热门文章
- 记录一次“迅雷的不要脸行为”--取消浏览器响应下载
- MyEclipse注册码生成器
- 网络拓扑设计----hcip
- CAD2000坐标系转GIS84投影坐标系
- 计算机辅助技术基础知识点,计算机辅助设计技术基础知识.PPT
- CRC原理简析——史上最清新脱俗简单易懂的CRC解析
- Glass-theme cod mw2 for win7|Windows7主题下载
- 九、PHP知识核心 面向对象
- 2022年合束器市场前景分析及研究报告
- USB3.0和2.0有什么区别 --旧时光 oldtimeblog