序
之前寫了一篇restTemplate使用實例洞就,由于spring 5全面引入reactive擅羞,同時也有了restTemplate的reactive版webclient轴脐,本文就來對應展示下webclient的基本使用堪藐。
請求攜帶header
- 攜帶cookie
@Test
public void testWithCookie(){
Mono<String> resp = WebClient.create()
.method(HttpMethod.GET)
.uri("http://baidu.com")
.cookie("token","xxxx")
.cookie("JSESSIONID","XXXX")
.retrieve()
.bodyToMono(String.class);
LOGGER.info("result:{}",resp.block());
}
- 攜帶basic auth
@Test
public void testWithBasicAuth(){
String basicAuth = "Basic "+ Base64.getEncoder().encodeToString("user:pwd".getBytes(StandardCharsets.UTF_8));
LOGGER.info(basicAuth);
Mono<String> resp = WebClient.create()
.get()
.uri("http://baidu.com")
.header(HttpHeaders.AUTHORIZATION,basicAuth)
.retrieve()
.bodyToMono(String.class);
LOGGER.info("result:{}",resp.block());
}
- 設置全局user-agent
@Test
public void testWithHeaderFilter(){
WebClient webClient = WebClient.builder()
.defaultHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
.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();
Mono<String> resp = webClient.get()
.uri("https://baidu.com")
.retrieve()
.bodyToMono(String.class);
LOGGER.info("result:{}",resp.block());
}
get請求
- 使用placeholder傳遞參數
@Test
public void testUrlPlaceholder(){
Mono<String> resp = WebClient.create()
.get()
//多個參數也可以直接放到map中,參數名與placeholder對應上即可
.uri("http://www.baidu.com/s?wd={key}&other={another}","北京天氣","test") //使用占位符
.retrieve()
.bodyToMono(String.class);
LOGGER.info("result:{}",resp.block());
}
- 使用uriBuilder傳遞參數
@Test
public void testUrlBiulder(){
Mono<String> resp = WebClient.create()
.get()
.uri(uriBuilder -> uriBuilder
.scheme("http")
.host("www.baidu.com")
.path("/s")
.queryParam("wd", "北京天氣")
.queryParam("other", "test")
.build())
.retrieve()
.bodyToMono(String.class);
LOGGER.info("result:{}",resp.block());
}
post表單
@Test
public 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);
LOGGER.info("result:{}",resp.block());
}
post json
- 使用bean來post
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;
}
}
@Test
public 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());
}
- 直接post raw json
@Test
public void testPostRawJson(){
Mono<String> resp = WebClient.create().post()
.uri("http://localhost:8080/demo/json")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject("{\n" +
" \"title\" : \"this is title\",\n" +
" \"author\" : \"this is author\"\n" +
"}"))
.retrieve().bodyToMono(String.class);
LOGGER.info("result:{}",resp.block());
}
post二進制--上傳文件
@Test
public 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());
}
下載二進制
- 下載圖片
@Test
public void testDownloadImage() throws IOException {
Mono<Resource> resp = WebClient.create().get()
.uri("http://www.toolip.gr/captcha?complexity=99&size=60&length=9")
.accept(MediaType.IMAGE_PNG)
.retrieve().bodyToMono(Resource.class);
Resource resource = resp.block();
BufferedImage bufferedImage = ImageIO.read(resource.getInputStream());
ImageIO.write(bufferedImage, "png", new File("captcha.png"));
}
- 下載文件
@Test
public void testDownloadFile() throws IOException {
Mono<ClientResponse> resp = WebClient.create().get()
.uri("http://localhost:8080/file/download")
.accept(MediaType.APPLICATION_OCTET_STREAM)
.exchange();
ClientResponse response = resp.block();
String disposition = response.headers().asHttpHeaders().getFirst(HttpHeaders.CONTENT_DISPOSITION);
String fileName = disposition.substring(disposition.indexOf("=")+1);
Resource resource = response.bodyToMono(Resource.class).block();
File out = new File(fileName);
FileUtils.copyInputStreamToFile(resource.getInputStream(),out);
LOGGER.info(out.getAbsolutePath());
}
錯誤處理
@Test
public void testRetrieve4xx(){
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 -> {
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);
}
- 可以使用onStatus根據status code進行異常適配
- 可以使用doOnError異常適配
- 可以使用onErrorReturn返回默認值
小結
webclient是新一代的async rest template滋恬,api也相對簡潔匀哄,而且是reactive的秦效,非常值得使用。