Spring webflux 过滤器:如何在查询执行后获取反应器上下文?

Spring webflux filter: How to get the reactor context after the query execution?

Spring 引导 2.1.5 反应堆项目 3.2.9

在我的 webflux 项目中,我广泛使用反应器上下文来传递一些值。

我设置了一个过滤器,并尝试记录上下文中的内容,并在 error/success 的情况下记录不同的内容。

我查看了这个文档:https://projectreactor.io/docs/core/release/reference/#context

我仍然在努力(尤其是在错误方面)得到它。

基本上,我有这个过滤器:

@Component
public class MdcWebFilter implements WebFilter {

    @NotNull
    @Override
    public Mono<Void> filter(@NotNull ServerWebExchange serverWebExchange,
                             WebFilterChain webFilterChain) {

        Mono<Void> filter = webFilterChain.filter(serverWebExchange);

        return filter
            .doAfterSuccessOrError(new BiConsumer<Void, Throwable>() {
                @Override
                public void accept(Void aVoid, Throwable throwable) {
                    //Here i would like to be able to access to the request's context
                    System.out.println("doAfterSuccessOrError:" + (throwable==null ? "OK" : throwable.getMessage())+"log the context");
                }
            })
            .doOnEach(new Consumer<Signal<Void>>() {
                @Override
                public void accept(Signal<Void> voidSignal) {
                    //Here i have the context but i don't really know if i am in success or error
                    System.out.println("doOnEach:"+"Log OK/KO and the exception" + voidSignal.getContext());
                }
            })
            .subscriberContext(context -> context.put("somevar", "whatever"));
    }

}

我也尝试过使用 flatMap() 和 Mono.subscriberContext(),但我不确定如何正确插入过滤器(尤其是错误的)。

实现此目标的最佳方法是什么?

我知道这可能不是最干净的解决方案,但您可以创建一个容器 class 来保持两个回调之间的上下文。

您可以将上下文存储在 doOnEach,然后您可以在 doAfterSuccessOrError:

加载它
    public Mono<Void> filter(@NotNull ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {

        @lombok.Data
        class MyContextContainer {
            private Context context;
        }

        MyContextContainer container = new MyContextContainer();

        Mono<Void> filter = webFilterChain.filter(serverWebExchange);

        return filter
            .doAfterSuccessOrError(new BiConsumer<Void, Throwable>() {
                @Override
                public void accept(Void aVoid, Throwable throwable) {
                    // load the context here
                    Context context = container.getContext();
                    // then do your stuff here
                }
            })
            .doOnEach(new Consumer<Signal<Void>>() {
                @Override
                public void accept(Signal<Void> voidSignal) {
                    // store the context here
                    container.setContext(voidSignal.getContext());
                }
            })
            .subscriberContext(context -> context.put("somevar", "whatever"));
    }

不需要 class,真的。它可能是 AtomicReference,但你明白了。

同样,这可能只是一种解决方法。我相信一定有更好的方法来访问上下文。

我不确定是否可以从 WebFilter 中访问请求反应器上下文。 WebFilter 上下文存在于另一个 Mono 链中。 但是可以将属性与请求相关联并能够在请求生命周期内获取这些属性 RequestContextHolder for Reactive Web 非常类似于 Servlet API.

控制器:

@GetMapping(path = "/v1/customers/{customerId}")
public Mono<Customer> getCustomerById(
    @PathVariable("customerId") String customerId,
    ServerWebExchange serverWebExchange)
{
  serverWebExchange.getAttributes().put("traceId", "your_trace_id");
  return customerService.findById(customerId);
}

网络过滤器:

public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    // ...
    String traceId = exchange.getAttributeOrDefault("traceId", "default_value_goes_here");
    //...
    return chain.filter(exchange);
}