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

时间:2019-06-13 06:42:57

标签: spring-webflux project-reactor

Spring Boot 2.1.5 Project Reactor 3.2.9

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

我设置了一个过滤器,并试图记录上下文中的内容,并在发生错误/成功的情况下记录不同的内容。

我已经查看了以下文档: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(),但是我不确定如何正确插入过滤器(尤其是错误的消息)。

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

2 个答案:

答案 0 :(得分:1)

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

您可以将上下文存储在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"));
    }

确实不需要上课。可能是AtomicReference,但您明白了。

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

答案 1 :(得分:1)

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

控制器:

    Token.update(
    {
        updatedAt: new Date
    },
    {
        where: {
            token_id: token
        }
    }
).then(value1 => {
        console.log('Token UpdatedAt has been updated');
    }
)

WebFilter:

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