简介
WebClient是Spring5中新引入的非阻塞、反应式HttpClient框架。
与传统的RestTemplate相比的话,WebClient具有以下优势:
- 非阻塞 I/O。
- 反应流背压。
- 具有高并发性,硬件资源消耗更少。
- 流畅的API设计。
- 同步和异步交互。
- 流式传输支持
使用
1.创建webclient
WebClient.builder()
.clientConnector(new JettyClientHttpConnector())
.baseUrl(baseUrl)
.filter(logRequest())
.exchangeStrategies(ExchangeStrategies.builder()
.codecs(clientCodecConfigurer -> clientCodecConfigurer.defaultCodecs()
.maxInMemorySize(16 * 1024 * 1024))
.build())
.build();
clientConnector :客户端连接实现,默认使用Netty,同时也内置Jetty的实现,当然也可以自定义实现clientConnector接口定义新的底层库。
public interface ClientHttpConnector {
/**
* Connect to the origin server using the given {@code HttpMethod} and
* {@code URI} and apply the given {@code requestCallback} when the HTTP
* request of the underlying API can be initialized and written to.
* @param method the HTTP request method
* @param uri the HTTP request URI
* @param requestCallback a function that prepares and writes to the request,
* returning a publisher that signals when it's done writing.
* Implementations can return a {@code Mono<Void>} by calling
* {@link ClientHttpRequest#writeWith} or {@link ClientHttpRequest#setComplete}.
* @return publisher for the {@link ClientHttpResponse}
*/
Mono<ClientHttpResponse> connect(HttpMethod method, URI uri,
Function<? super ClientHttpRequest, Mono<Void>> requestCallback);
}
baseUrl : 配置一个用于请求的基础url,通常用于配置域名。
filter : 配置过滤器,可以配置多个,常用的有请求前和请求后,作为权限验证及日志记录等。
//用于请求前,将请求方法及url打印出来
private ExchangeFilterFunction logRequest() {
return (clientRequest, next) -> {
log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
clientRequest.headers()
.forEach((name, values) -> values.forEach(value -> log.info("Request: {}={}", name, value)));
return next.exchange(clientRequest);
};
}
exchangeStrategies:配置需要使用的exchange,这里主要是重新配置在解析响应结果时可用的内存大小,因为默认大小只有32k,随便一个请求的返回值就超了,所以这里需要重新设置一下大小。当然这个方法还可以设置其他配置,详情可以查文档
2.使用
GET方法
webClient.get()
.uri(url, (Object[]) param)
.accept(mediaType)
.exchangeToMono(response -> {
if (response.statusCode().equals(HttpStatus.OK)) {
return response.bodyToMono(tClass).doOnNext(item -> {
log.info("Get Response :{}", JSONObject.toJSONString(item));
});
} else {
return Mono.error(new ServerException(ResponseCode.API_ERROR));
}
});
uri 中有两个参数,第一个是请求的地址,第二个是请求参数,这种的写法参数是路径变量,类似rest风格,当然也有其他的写法。
1.
webClient.get().uri(uriBuilder -> uriBuilder.path("/user/{id}/{name}")
.build(id, name));
2.
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("id", "id1");
uriVariables.put("name", "name1");
webClient.get().uri("/user/{id}/{name}", uriVariables)
accept 中是设置请求的contentType,内置MediaType类中有定义好的常量直接引用接口
public static final MediaType APPLICATION_JSON;
public static final String APPLICATION_JSON_VALUE = "application/json";
exchangeToMono 中就是对响应结果的处理,可以在里面编写自己的业务逻辑。
POST方法
//一个jsonPost请求
webClient.post()
.uri(expand)
.contentType(MediaType.APPLICATION_JSON)
.body(Mono.just(ParamsUtil.ObjectToMap(body)), Map.class)
.exchangeToMono(clientResponse -> {
MediaType mediaType = clientResponse.headers().contentType().orElse(MediaType.APPLICATION_JSON);
log.info("PostJson resp headers :{},statusCode:{}", mediaType, clientResponse.statusCode());
if (clientResponse.statusCode().equals(HttpStatus.OK)) {
return clientResponse.bodyToMono(String.class).doOnNext(item -> {
log.debug("PostJson Response :{}", JSONObject.toJSONString(item));
}).flatMap(str -> Mono.just(JSONObject.parseObject(str, tClass)));
} else {
return clientResponse.bodyToMono(String.class).doOnNext(item -> {
log.info("PostJson Response :{}", JSONObject.toJSONString(item));
}).flatMap(item -> Mono.error(new ServerException(ResponseCode.API_ERROR)));
}
});
post方法跟get方法大同小异,注意区别在于传参,post方法中需要将参数放入body中,用mono包装,这里支持任意数据类型可以试java对象,也可以是map