此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.0spring-doc.cadn.net.cn

过滤器

您可以注册客户端筛选器 (ExchangeFilterFunction) 通过WebClient.Builder为了拦截和修改请求,如下例所示:spring-doc.cadn.net.cn

WebClient client = WebClient.builder()
		.filter((request, next) -> {

			ClientRequest filtered = ClientRequest.from(request)
					.header("foo", "bar")
					.build();

			return next.exchange(filtered);
		})
		.build();

这可用于横切关注点,例如身份验证。以下示例使用 通过静态工厂方法进行基本身份验证的过滤器:spring-doc.cadn.net.cn

import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;

WebClient client = WebClient.builder()
		.filter(basicAuthentication("user", "password"))
		.build();

过滤器可以通过改变现有的WebClient实例,结果 在新的WebClient实例,该实例不会影响原始实例。例如:spring-doc.cadn.net.cn

import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;

WebClient client = webClient.mutate()
		.filters(filterList -> {
			filterList.add(0, basicAuthentication("user", "password"));
		})
		.build();

WebClient是围绕过滤器链的薄立面,后跟一个ExchangeFunction.它提供了一个工作流来发出请求、编码到更高级别或从更高级别编码 level 对象,它有助于确保始终使用响应内容。 当 filter 以某种方式处理响应时,必须格外小心地始终使用 其内容,或者以其他方式将其传播到下游的WebClient这将确保 一样。下面是一个处理UNAUTHORIZED状态代码,但确保 任何响应内容(无论是否预期)都会被释放:spring-doc.cadn.net.cn

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);
		}
	});
}

下面的示例演示了如何使用ExchangeFilterFunction要创建的接口 一个自定义 Filter 类,有助于计算Content-Length标头PUTPOST multipart/form-data请求。spring-doc.cadn.net.cn

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));
            });
        }
    }
}

APP信息