Spring WebFlux - WebFilter 中的 statusCode 为空
Spring WebFlux - statusCode is null in a WebFilter
目前正在使用 Spring Boot 2.0.4 开发一个 WebFilter,它在 Spring WebFlux 应用程序中记录 HTTP 请求和响应信息(URL、响应状态代码、持续时间等) .
工作正常,除了 exchange.getResponse().getStatusCode() 保持为空.
尝试了两种不同的 filter(ServerWebExchange exchange, WebFilterChain chain),第一个:
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;
还在过滤器上尝试了各种顺序:none、@Order(100000)、@Order(-100000)。
状态代码保持为空。
什么是正确的实施方式?
更新 1
写了一个基于 KevH 解决方案的简约工作示例,参见 https://github.com/fbeaufume/webflux-filter-sample 但它还没有工作,状态仍然为空。 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(或类似方法)的过滤器实现是正确的,但这正确检索了 HTTP 响应状态 只有使用 REST 控制器方法 return 类型中的 ResponseEntity。
不支持(即状态为空):void
、Mono<Void>
、String
、Mono<String>
、MyBean
、Mono<MyBean>
、等等
支持(即状态为200):ReponseEntity<Void>
、Mono<ResponseEntity<Void>>
、ReponseEntity<String>
、Mono<ResponseEntity<String>>
、ReponseEntity<MyBean>
、Mono<ResponseEntity<MyBean>>
等.
错误?
Spring Boot 2.0.5 具有相同的行为。
更新 3
打开了关于该主题的问题,请参阅 https://jira.spring.io/browse/SPR-17368
你可以试试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));
}
示例输出
Request[/logrequest] started,
trace_id[b45b550a-b9ad-4323-a850-cb085a78a086] Request[/logrequest],
completed, status_code[202], time[41],
trace_id[b45b550a-b9ad-4323-a850-cb085a78a086]
编辑:不确定为什么这不起作用,但这里有两个解决方法
都有一个 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));
}
目前正在使用 Spring Boot 2.0.4 开发一个 WebFilter,它在 Spring WebFlux 应用程序中记录 HTTP 请求和响应信息(URL、响应状态代码、持续时间等) .
工作正常,除了 exchange.getResponse().getStatusCode() 保持为空.
尝试了两种不同的 filter(ServerWebExchange exchange, WebFilterChain chain),第一个:
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;
还在过滤器上尝试了各种顺序:none、@Order(100000)、@Order(-100000)。
状态代码保持为空。
什么是正确的实施方式?
更新 1
写了一个基于 KevH 解决方案的简约工作示例,参见 https://github.com/fbeaufume/webflux-filter-sample 但它还没有工作,状态仍然为空。 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(或类似方法)的过滤器实现是正确的,但这正确检索了 HTTP 响应状态 只有使用 REST 控制器方法 return 类型中的 ResponseEntity。
不支持(即状态为空):void
、Mono<Void>
、String
、Mono<String>
、MyBean
、Mono<MyBean>
、等等
支持(即状态为200):ReponseEntity<Void>
、Mono<ResponseEntity<Void>>
、ReponseEntity<String>
、Mono<ResponseEntity<String>>
、ReponseEntity<MyBean>
、Mono<ResponseEntity<MyBean>>
等.
错误?
Spring Boot 2.0.5 具有相同的行为。
更新 3
打开了关于该主题的问题,请参阅 https://jira.spring.io/browse/SPR-17368
你可以试试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));
}
示例输出
Request[/logrequest] started, trace_id[b45b550a-b9ad-4323-a850-cb085a78a086] Request[/logrequest], completed, status_code[202], time[41], trace_id[b45b550a-b9ad-4323-a850-cb085a78a086]
编辑:不确定为什么这不起作用,但这里有两个解决方法 都有一个 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));
}