過濾器
您可以透過 WebClient.Builder 註冊一個客戶端過濾器 (ExchangeFilterFunction) 來攔截和修改請求,如下例所示
-
Java
-
Kotlin
WebClient client = WebClient.builder()
.filter((request, next) -> {
ClientRequest filtered = ClientRequest.from(request)
.header("foo", "bar")
.build();
return next.exchange(filtered);
})
.build();
val client = WebClient.builder()
.filter { request, next ->
val filtered = ClientRequest.from(request)
.header("foo", "bar")
.build()
next.exchange(filtered)
}
.build()
這可用於橫切關注點,例如身份驗證。以下示例使用靜態工廠方法透過過濾器進行基本身份驗證
-
Java
-
Kotlin
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
WebClient client = WebClient.builder()
.filter(basicAuthentication("user", "password"))
.build();
import org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication
val client = WebClient.builder()
.filter(basicAuthentication("user", "password"))
.build()
可以透過修改現有的 WebClient 例項來新增或刪除過濾器,從而生成一個不影響原始例項的新 WebClient 例項。例如
-
Java
-
Kotlin
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
WebClient client = webClient.mutate()
.filters(filterList -> {
filterList.add(0, basicAuthentication("user", "password"));
})
.build();
val client = webClient.mutate()
.filters { it.add(0, basicAuthentication("user", "password")) }
.build()
WebClient 是過濾器鏈之上的一個薄薄的門面,然後是 ExchangeFunction。它提供了一個工作流來發出請求,將高階物件編碼和解碼,並有助於確保響應內容始終被消費。當過濾器以某種方式處理響應時,必須格外小心,始終消費其內容,或者以其他方式將其向下傳播到 WebClient,後者將確保同樣的事情。下面是一個處理 UNAUTHORIZED 狀態碼但確保任何響應內容(無論是否預期)都被釋放的過濾器
-
Java
-
Kotlin
public ExchangeFilterFunction renewTokenFilter() {
return (request, next) -> next.exchange(request).flatMap(response -> {
if (response.statusCode().value() == HttpStatus.UNAUTHORIZED.value()) {
return response.releaseBody()
.then(renewToken())
.flatMap(token -> {
ClientRequest newRequest = ClientRequest.from(request).build();
return next.exchange(newRequest);
});
} else {
return Mono.just(response);
}
});
}
fun renewTokenFilter(): ExchangeFilterFunction? {
return ExchangeFilterFunction { request: ClientRequest?, next: ExchangeFunction ->
next.exchange(request!!).flatMap { response: ClientResponse ->
if (response.statusCode().value() == HttpStatus.UNAUTHORIZED.value()) {
return@flatMap response.releaseBody()
.then(renewToken())
.flatMap { token: String? ->
val newRequest = ClientRequest.from(request).build()
next.exchange(newRequest)
}
} else {
return@flatMap Mono.just(response)
}
}
}
}
以下示例演示瞭如何使用 ExchangeFilterFunction 介面建立一個自定義過濾器類,該類透過緩衝來計算 PUT 和 POST multipart/form-data 請求的 Content-Length 標頭。
-
Java
-
Kotlin
public class MultipartExchangeFilterFunction implements ExchangeFilterFunction {
@Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
if (MediaType.MULTIPART_FORM_DATA.includes(request.headers().getContentType())
&& (request.method() == HttpMethod.PUT || request.method() == HttpMethod.POST)) {
return next.exchange(ClientRequest.from(request).body((outputMessage, context) ->
request.body().insert(new BufferingDecorator(outputMessage), context)).build()
);
} else {
return next.exchange(request);
}
}
private static final class BufferingDecorator extends ClientHttpRequestDecorator {
private BufferingDecorator(ClientHttpRequest delegate) {
super(delegate);
}
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
return DataBufferUtils.join(body).flatMap(buffer -> {
getHeaders().setContentLength(buffer.readableByteCount());
return super.writeWith(Mono.just(buffer));
});
}
}
}
class MultipartExchangeFilterFunction : ExchangeFilterFunction {
override fun filter(request: ClientRequest, next: ExchangeFunction): Mono<ClientResponse> {
return if (MediaType.MULTIPART_FORM_DATA.includes(request.headers().getContentType())
&& (request.method() == HttpMethod.PUT || request.method() == HttpMethod.POST)) {
next.exchange(ClientRequest.from(request)
.body { message, context -> request.body().insert(BufferingDecorator(message), context) }
.build())
}
else {
next.exchange(request)
}
}
private class BufferingDecorator(delegate: ClientHttpRequest) : ClientHttpRequestDecorator(delegate) {
override fun writeWith(body: Publisher<out DataBuffer>): Mono<Void> {
return DataBufferUtils.join(body)
.flatMap {
headers.contentLength = it.readableByteCount().toLong()
super.writeWith(Mono.just(it))
}
}
}
}