在Spring boot webflux 中实现 RequestContextHolder,主要目的是为了feign在调用服务时,先获取当前request,然后获取request的header,然后复制给RequestTemplate,进一步传递到服务,给被调用的服务使用。

为什么要像下面这样整这么麻烦,因为RequestContextHolder . getRequestAttributes ( )在webflux 不能用了

具体代码如下:

定义一个filter,把当前请求放入到上下文

package cn.nezhacloud.fireTippedSpear.gateway.filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;
@Configuration
public class ReactiveRequestContextFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
        return webFilterChain.filter(serverWebExchange).subscriberContext((c) -> c.hasKey(ReactiveRequestContextHolder.CONTEXT_KEY) ? c : this.withContext(c, serverWebExchange));
    private Context withContext(Context mainContext, ServerWebExchange exchange) {
        return mainContext.putAll(Context.of(ReactiveRequestContextHolder.CONTEXT_KEY,exchange.getRequest()));

再定义一个contextHolder

package cn.nezhacloud.fireTippedSpear.gateway.filter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;
import java.util.function.Function;
public class ReactiveRequestContextHolder {
    public static final String CONTEXT_KEY = "ServerHttpRequest";
    public ReactiveRequestContextHolder() {
    public static Mono<ServerHttpRequest> getContext() {
        return Mono.subscriberContext().filter((c) -> {
            return c.hasKey(CONTEXT_KEY);
        }).flatMap((c) -> {
            return (Mono)c.get(CONTEXT_KEY);
    public static Function<Context, Context> clearContext() {
        return (context) -> {
            return context.delete(CONTEXT_KEY);

定义feign调用的RequestInterceptor

package cn.nezhacloud.fireTippedSpear.gateway.config;
import cn.nezhacloud.fireTippedSpear.gateway.filter.ReactiveRequestContextHolder;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignRequestConfig implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        //ReactiveRequestContextHolder.getContext().block() 得到的值为null
        ReactiveRequestContextHolder.getContext().doOnSuccess(request->{
            //这一句不会执行
            System.out.println("okkkkkkkkk");
                                

第一种方式  

  public void apply(RequestTemplate requestTemplate) {
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder
                .getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String x'x= request.getHeader("xx");

        requestTemplate.header("xx", xx);
       
    }

但是请记住,坏处是在循环调用feign 的时候 header中的值会丢失,好处是一个方法全部通用

第二种方式

使用@RequestHeader 注解直接写在你feign接口中

String xx(@RequestParam("xx")  String xx, @RequestHeader("xx") String xx);

这样的方式的坏处就是每个接口需要header值的话都需要写,好处就是header中的值不会丢失, 简单粗暴

评论 (0)