過濾器

您可以透過 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 介面建立一個自定義過濾器類,該類透過緩衝幫助計算 PUTPOST 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))
				}
		}
	}
}