有了 WebClient 还在用 RestTemplate?
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
直接初始化,不加任何参数。
WebClient client = WebClient.create();
初始化时,提供一个默认的调用地址。
WebClient client = WebClient.create("http://localhost:8080");
自定义参数初始化。
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();
修改默认的超时时间。
//通过 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 请求
以阻塞的方式获取 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());以阻塞方式获取多个返回结果,用
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());});
通过非阻塞方式获取响应结果
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());});
通过占位符传参
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 请求
发起
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());使用
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论