sunny906 2024-11-15 13:45 采纳率: 80%
浏览 24
已结题

WebFlux丢失context

在controller里调用 ctx.get("ServerHttpRequest.class") 的时候报错 Context is empty,请问问题出在哪里呢?


@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)
                .contextWrite(ctx -> ctx.put("ServerHttpRequest.class", request));
    }
}

public class ReactiveRequestContextHolder {
    public static Mono<ServerHttpRequest> getRequest() {
        return Mono.deferContextual(ctx ->
        {
            // -------------------------------------------
            // reactor.core.Exceptions$ErrorCallbackNotImplemented: java.util.NoSuchElementException: Context is empty
            // -------------------------------------------
            return Mono.just(ctx.get("ServerHttpRequest.class"));
        });
    }
}

    @GetMapping(value = "/test")
    public Mono<String> get() {
        Mono<ServerHttpRequest> req = ReactiveRequestContextHolder.getRequest();
        req.flatMap(request -> {
            HttpHeaders headers = request.getHeaders();
            return Mono.just(headers.getFirst("user"));
        }).subscribe(s -> System.out.println(s));
        return Mono.just("ok");
    }
  • 写回答

1条回答 默认 最新

  • Hello.Reader 优质创作者: 大数据、人工智能、编程技术技术领域 2024-11-15 13:50
    关注

    这个错误 (reactor.core.Exceptions$ErrorCallbackNotImplemented: java.util.NoSuchElementException: Context is empty) 的原因在于在调用 ReactiveRequestContextHolder.getRequest() 的时候,当前的 Reactor Context 并不包含 ServerHttpRequest 实例。即,Context 是空的,因为 ctx.put("ServerHttpRequest.class", request)WebFilter 中被写入的上下文没有被传播到 @GetMapping 的调用链中。

    解决方法

    要确保 Reactor Context 被正确地传播到控制器方法调用中,你可以采用以下方法来修复这个问题:

    1. 确保 WebFilter 被正确注册并生效
      确保 ReactiveRequestContextFilter 已经在 Spring WebFlux 的过滤器链中生效,因为这是将 ServerHttpRequest 添加到 Context 的关键步骤。

    2. 在控制器方法中保持 Mono 的链式结构
      为了确保上下文被传播,避免使用 subscribe() 方法。subscribe() 方法会立即触发流的执行,使得 Context 中的数据可能无法传播到执行链中。相反,使用链式 Mono 操作来获取上下文数据。

    可以修改 get() 方法,使其返回一个 Mono<String> 的结果,而不是使用 subscribe()

    @GetMapping(value = "/test")
    public Mono<String> get() {
        return ReactiveRequestContextHolder.getRequest()
                .flatMap(request -> {
                    HttpHeaders headers = request.getHeaders();
                    return Mono.justOrEmpty(headers.getFirst("user"));
                })
                .defaultIfEmpty("ok");
    }
    

    解释

    这种修改方式避免了使用 subscribe(),确保 Context 能在 WebFilter 设置后正确传播到 ReactiveRequestContextHolder.getRequest() 中。

    额外说明

    如果你必须在控制器方法之外的地方访问 Context 数据,请确保在 Mono 的执行链上,且上下文传播已经启用。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 11月15日
  • 已采纳回答 11月15日
  • 创建了问题 11月15日