Spring WebFlux-WebFilter中的statusCode为null

时间:2018-10-05 14:38:24

标签: java spring-boot spring-webflux

当前正在使用Spring Boot 2.0.4开发一个WebFilter,用于在Spring WebFlux应用程序中记录HTTP请求和响应信息(URL,响应状态代码,持续时间等)。

工作正常,除了 exchange.getResponse()。getStatusCode()保持为空

尝试了 filter(ServerWebExchange交换,WebFilterChain链)的两个不同的符号,第一个:

return chain.filter(exchange).doAfterTerminate(
        () -> System.out.println("Status=" + exchange.getResponse().getStatusCode()));

第二个:

    Mono<Void> filtered = chain.filter(exchange);
    exchange.getResponse().beforeCommit(() -> {
        System.out.println("Status=" + exchange.getResponse().getStatusCode());
        return Mono.empty();
    });
    return filtered;

还在过滤器上尝试了各种订单:无,@ Order(100000),@ Order(-100000)。

状态代码保持为空。

什么是正确的实现方式?

更新1

编写了一个基于KevH解决方案的简约工作示例,请参见https://github.com/fbeaufume/webflux-filter-sample,但它仍无法正常工作,状态仍然为null。 MWE公开了两个REST端点:/ hello和/ pause / 1000(暂停1秒)。

请注意,当调用暂停端点日志时:

11:06:20.644  INFO 9536 --- [ctor-http-nio-2] com.adeliosys.sample.LogFilter           : Request [/pause/1000] started, traceId [bb3fe67d-170b-4070-837d-816fe1420a1f]
11:06:20.673  INFO 9536 --- [ctor-http-nio-2] com.adeliosys.sample.HelloController     : Pausing for 1000 msec
11:06:21.683  INFO 9536 --- [     parallel-2] com.adeliosys.sample.LogFilter           : Request [/pause/1000] completed, statusCode [null], time [1039], traceId [bb3fe67d-170b-4070-837d-816fe1420a1f]
11:06:21.684  INFO 9536 --- [     parallel-2] com.adeliosys.sample.HelloController     : Paused for 1000 msec

令我惊讶的是,过滤器的第二条消息显示在端点的第二条消息之前。

更新2

似乎使用doAfterTerminate(或类似方法)的过滤器实现是正确的,但这只能使用 ResponseEntity 的 REST控制器方法正确地检索HTTP响应状态。在返回类型中。

不支持(即状态为空):voidMono<Void>StringMono<String>MyBeanMono<MyBean>等。

受支持(即状态为200):ReponseEntity<Void>Mono<ResponseEntity<Void>>ReponseEntity<String>Mono<ResponseEntity<String>>ReponseEntity<MyBean>Mono<ResponseEntity<MyBean>>等< / p>

臭虫?

Spring Boot 2.0.5具有相同的行为。

更新3

打开了与此主题相关的问题,请参见https://jira.spring.io/browse/SPR-17368

1 个答案:

答案 0 :(得分:1)

您可以尝试doAfterSuccessOrError

@Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String traceId = UUID.randomUUID().toString();
        long startTime = System.currentTimeMillis();
        String path = exchange.getRequest().getURI().getPath();
        System.out.printf("Request[%s] started, trace_id[%s]", path, traceId);

        return chain.filter(exchange)
                .doAfterSuccessOrError((r, t) -> {
                    System.out.printf("Request[%s], completed, status_code[%s], time[%d], trace_id[%s]", path,
                            exchange.getResponse().getStatusCode(), System.currentTimeMillis() - startTime, traceId);
                })
                .subscriberContext(Context.of(String.class, traceId));
    }

样本输出

  

请求[/ logrequest]已启动,   trace_id [b45b550a-b9ad-4323-a850-cb085a78a086]请求[/ logrequest],   已完成,状态码[202],时间[41],   trace_id [b45b550a-b9ad-4323-a850-cb085a78a086]

edit:不确定到底为什么不起作用,但是这里有两个解决方法 都有一个ResponseEntity

@GetMapping("/hello")
    public Mono<ResponseEntity<String>> publicHello() {
        return Mono.just(ResponseEntity.ok("Hello world!"));
    }

    @GetMapping("/pause/{duration}")
    @ResponseStatus(HttpStatus.ACCEPTED)
    public Mono<Void> pause2(@PathVariable long duration) {
        LOGGER.info("Pausing for {} msec", duration);
        return (duration > 0 ? Mono.delay(Duration.ofMillis(duration)) : Mono.empty())
                .then()
                .doFinally(s -> LOGGER.info("Paused for {} msec", duration));
    }