使用 Spring Boot WebClient 时如何拦截请求

Posted

技术标签:

【中文标题】使用 Spring Boot WebClient 时如何拦截请求【英文标题】:How to intercept a request when using SpringBoot WebClient 【发布时间】:2019-01-14 13:16:57 【问题描述】:

我正在尝试使用WebClient 来调用我的restServices。以前在RestTemplate 上,我们定义了ClientHttpRequestInterceptor 并将其附加到RestTemplate 以拦截和修改请求。使用WebClient,有没有办法做到这一点?

谢谢,

-Sreeni

【问题讨论】:

【参考方案1】:

当您使用 WebClient Builder 时,您可以使用 filter() 方法传入 ExchangeFilterFunction 接口的实现。这相当于ClientHttpRequestInterceptorRestTemplate

WebClient 构建器文档:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.Builder.html#filter-org.springframework.web.reactive.function.client.ExchangeFilterFunction-

ExchangeFilterFunction 文档:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/function/client/ExchangeFilterFunction.html

例如:

WebClient webClient = WebClient.builder()
        .baseUrl("http://localhost:8080|)
        .filter(logFilter())
        .build();


private ExchangeFilterFunction logFilter() 
    return (clientRequest, next) -> 
        logger.info("External Request to ", clientRequest.url());
        return next.exchange(clientRequest);
    ;

【讨论】:

我知道这在某种意义上可能相当于ClientHttpRequestInterceptor,但它并没有给你太多的访问权限。例如,您看不到请求的正文。【参考方案2】:

在我的情况下,我需要从传入请求中获取一些标头并将它们放入我的请求中。我找到了我需要的东西here。

首先需要一个过滤器

/**
 * ReactiveRequestContextFilter
 *
 * @author L.cm
 */
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class ReactiveRequestContextFilter implements WebFilter 

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) 
        ServerHttpRequest request = exchange.getRequest();
        return chain.filter(exchange)
        .subscriberContext(ctx -> ctx.put(ReactiveRequestContextHolder.CONTEXT_KEY, request));
    

还有ReactiveRequestContextHolder

/**
 * ReactiveRequestContextHolder
 *
 * @author L.cm
 */
public class ReactiveRequestContextHolder 
    static final Class<ServerHttpRequest> CONTEXT_KEY = ServerHttpRequest.class;

    /**
     * Gets the @code Mono<ServerHttpRequest> from Reactor @link Context
     * @return the @code Mono<ServerHttpRequest>
     */
    public static Mono<ServerHttpRequest> getRequest() 
        return Mono.subscriberContext()
            .map(ctx -> ctx.get(CONTEXT_KEY));
    


最后像 Michael McFadyen 说你需要配置一个 ExchangeFilterFunction,在我的情况下我需要 Auth 和 origin:

private ExchangeFilterFunction headerFilter() 
    return (request, next) -> ReactiveRequestContextHolder.getRequest()
            .flatMap(r -> 
                ClientRequest clientRequest = ClientRequest.from(request)
                        .headers(headers -> 
                            headers.set(HttpHeaders.ORIGIN, r.getHeaders().getFirst(HttpHeaders.ORIGIN));
                            headers.set(HttpHeaders.AUTHORIZATION, r.getHeaders().getFirst(HttpHeaders.AUTHORIZATION));
                        )
                        .build();
                return next.exchange(clientRequest);
            );

【讨论】:

【参考方案3】:

您可以使用 ExchangeFilterFunction 并在您正在使用的 WebClient 实例上配置它。有关更多信息,请参阅Spring Framework reference documentation。

【讨论】:

以上是关于使用 Spring Boot WebClient 时如何拦截请求的主要内容,如果未能解决你的问题,请参考以下文章

禁用 Spring Boot Webclient 日志

Spring Boot响应式WebClient调用遗留端点

Spring Boot:如何使用 WebClient 而不是 RestTemplate 来执行非阻塞和异步调用

使用 Spring Boot webclient 反序列化 OffsetDateTime

Spring boot 2 WebClient在订阅者中获取上下文参数

如何使用 Spring Boot WebClient 收集分页 API 响应?