diff --git a/spring/webflux/webclient.md b/spring/webflux/webclient.md index 7507e41..42d8424 100644 --- a/spring/webflux/webclient.md +++ b/spring/webflux/webclient.md @@ -233,3 +233,165 @@ Mono result = client.get() .bodyToMono(Person.class); ``` +## Exchange +`exchangeToMono`方法和`exchangeToFlux`方法可以提供更精确的控制,例如在statusCode不同时,使用不同的方法来进行反序列化: +```java +Mono entityMono = client.get() + .uri("/persons/1") + .accept(MediaType.APPLICATION_JSON) + .exchangeToMono(response -> { + if (response.statusCode().equals(HttpStatus.OK)) { + return response.bodyToMono(Person.class); + } + else { + // Turn to error + return response.createError(); + } + }); +``` + +如上所示,当`exchangeToMono() and exchangeToFlux()`返回的flux或mono完成时,clientResponse对象将会被释放,从而避免内存和连接的泄露。 + +因而,`response`不能在更下游进行反序列化,`具体的反序列化过程由提供的方法来声明如何进行decode`。 + +## Request Body +对于webclient,可以加载任意异步类型对象到request body,例如`Mono`: +```java +Mono personMono = ... ; + +Mono result = client.post() + .uri("/persons/{id}", id) + .contentType(MediaType.APPLICATION_JSON) + .body(personMono, Person.class) + .retrieve() + .bodyToMono(Void.class); +``` +同样的,也可以加载多个异步对象,例如`Flux`类型 +```java +Flux personFlux = ... ; + +Mono result = client.post() + .uri("/persons/{id}", id) + .contentType(MediaType.APPLICATION_STREAM_JSON) + .body(personFlux, Person.class) + .retrieve() + .bodyToMono(Void.class); +``` + +当不想从异步类型的对象加载值,而是想加载已有的值时,可以使用`bodyValue`方法: +```java +Person person = ... ; + +Mono result = client.post() + .uri("/persons/{id}", id) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(person) + .retrieve() + .bodyToMono(Void.class); +``` + +### Form Data +如果想要发送form data,可以为body指定`MultiValueMap`类型的值,content会被自动设置为`application/x-www-form-urlencoded`。使用示例如下所示: +```java +MultiValueMap formData = ... ; + +Mono result = client.post() + .uri("/path", id) + .bodyValue(formData) + .retrieve() + .bodyToMono(Void.class); +``` + +同时,可以使用`BodyInserters`来构建form data,示例如下所示: +```java +Mono result = client.post() + .uri("/path", id) + .body(fromFormData("k1", "v1").with("k2", "v2")) + .retrieve() + .bodyToMono(Void.class); +``` + +### Multipart Data +为了发送multipart data,需要提供`MultiValueMap`类型的值,其中value类型为`代表part内容的object对象`或`代表part内容和headers的HttpEntity对象`。 + +`MutlipartBodyBuilder`提供了便捷的api,使用示例如下所示: +```java +MultipartBodyBuilder builder = new MultipartBodyBuilder(); +builder.part("fieldPart", "fieldValue"); +builder.part("filePart1", new FileSystemResource("...logo.png")); +builder.part("jsonPart", new Person("Jason")); +builder.part("myPart", part); // Part from a server request + +MultiValueMap> parts = builder.build(); +``` + +通常情况下,无需为每个part指定`Content-Type`,在`HttpMessageWriter`执行序列化操作时,会自动的设置ContentType类型,对于`Resources`类型,会根据文件拓展名来决定ContentType。 + +当`MutliValueMap`类型的值构建完成后,使用示例如下: +```java +MultipartBodyBuilder builder = ...; + +Mono result = client.post() + .uri("/path", id) + .body(builder.build()) + .retrieve() + .bodyToMono(Void.class); +``` + +除了使用`MultipartBodyBuilder`之外,还可以使用`BodyInserters`来构建multipart body,示例如下: +```java +Mono result = client.post() + .uri("/path", id) + .body(fromMultipartData("fieldPart", "value").with("filePart", resource)) + .retrieve() + .bodyToMono(Void.class); +``` + +### PartEvent +如果需要提供多个multipart data,可以使用`PartEvent`类型: +- form fields可以通过`FormPartEvent::create`来创建 +- file uploads可以通过`FormPartEvent::create`来创建 + +可以通过`Flux.concat`方法来对其进行拼接,示例如下所示: +```java +Resource resource = ... +Mono result = webClient + .post() + .uri("https://example.com") + .body(Flux.concat( + FormPartEvent.create("field", "field value"), + FilePartEvent.create("file", resource) + ), PartEvent.class) + .retrieve() + .bodyToMono(String.class); +``` + +## Filters +可以通过`WebClient.Builder`注册client filter,其可以针对请求进行拦截和修改,示例如下所示: +```java +WebClient client = WebClient.builder() + .filter((request, next) -> { + + ClientRequest filtered = ClientRequest.from(request) + .header("foo", "bar") + .build(); + + return next.exchange(filtered); + }) + .build(); +``` +其可被用作认证,示例如下: +```java +WebClient client = WebClient.builder() + .filter(basicAuthentication("user", "password")) + .build(); +``` +通过修改WebClient对象,filter可被添加和删除,修改后的新webClient对象并不会影响之前的对象,示例如下: +```java +WebClient client = webClient.mutate() + .filters(filterList -> { + filterList.add(0, basicAuthentication("user", "password")); + }) + .build(); +``` +