我有一个使用Spring Boot 2.0.0.M5 / 2.0.0.BUILD-SNAPSHOT的Spring Boot WebFlux应用程序。 我需要在所有日志中添加trace-id。
为了让它在WebFlux应用程序中运行,我尝试使用描述为here和here的WebFilter方法
@Component
public class TraceIdFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange).subscriberContext((Context context) ->
context.put(AuditContext.class, getAuditContext(exchange.getRequest().getHeaders()))
);
}
我的控制器
@GetMapping(value = "/some_mapping")
public Mono<ResponseEntity<WrappedResponse>> getResource(@PathVariable("resourceId") String id) {
Mono.subscriberContext().flatMap(context -> {
AuditContext auditContext = context.get(AuditContext.class);
...
});
我遇到的问题是过滤器方法永远不会被执行,并且没有设置上下文。我已经确认Webfilter是在启动时加载的。 还有什么需要让过滤器工作吗?
答案 0 :(得分:3)
事实证明这不起作用的原因是因为我依赖spring-boot-starter-web和spring-boot-starter-webflux。
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-webflux")
我添加spring-boot-starter-web的原因是因为我在删除依赖项时遇到以下异常
Caused by: java.io.FileNotFoundException: class path resource [org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.class] cannot be opened because it does not exist
at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:177) ~[spring-core-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:51) ~[spring-core-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:99) ~[spring-core-5.0.0.RELEASE.jar:5.0.0.RELEASE]
我发现我收到此错误的原因是因为我在EnableAutoConfiguration中有一个配置类的自定义启动启动器
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.x.y.z.MyConfiguration
此组件类在组件扫描期间也被拾取,这似乎会导致一些问题。 删除对spring-boot-starter-web的依赖后,WebFilter开始工作。
答案 1 :(得分:0)
我遇到了很多问题,因此希望可以对某人有所帮助。我的用例是验证请求上的签名。这需要我为PUT / POST解析请求正文。我看到的另一个主要用例是日志记录,因此以下内容也将有所帮助。
MiddlewareAuthenticator.java
@Component
public class MiddlewareAuthenticator implements WebFilter {
@Autowired private RequestValidationService requestValidationService;
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain chain) {
return requestValidationService.validate(serverWebExchange)
.flatMap(r -> chain.filter(r));
}
}
RequestValidationService.java
@Service
public class RequestValidationService {
private DataBuffer stringBuffer(String value) {
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
NettyDataBufferFactory nettyDataBufferFactory =
new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
}
private String getBody(DataBuffer bodyBytes) {
ByteBuffer currByteBuffer = bodyBytes.asByteBuffer();
byte[] currArr = new byte[currByteBuffer.remaining()];
currByteBuffer.get(currArr);
DataBufferUtils.release(bodyBytes);
return new String(currArr, StandardCharsets.UTF_8);
}
private ServerHttpRequestDecorator requestWrapper(ServerHttpRequest request, String bodyStr) {
URI uri = request.getURI();
ServerHttpRequest newRequest = request.mutate().uri(uri).build();
DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
Flux<DataBuffer> newBodyFlux = Flux.just(bodyDataBuffer);
ServerHttpRequestDecorator requestDecorator =
new ServerHttpRequestDecorator(newRequest) {
@Override
public Flux<DataBuffer> getBody() {
return newBodyFlux;
}
};
return requestDecorator;
}
public Mono<ServerWebExchange> validate(ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
Flux<DataBuffer> body = request.getBody();
Map<String, String> postParams = new HashMap<String, String>();
Flux<ServerWebExchange> processBodyIfExists = body.flatMap(
(DataBuffer curr) -> {
String bodyStr = getBody(curr);
postParams.put("X-Body", bodyStr);
return Flux.just(exchange
.mutate()
//since we read the buffer we have to wrap it to prevent double read
.request(requestWrapper(request, bodyStr))
.build());
})
// if no body exists just return the existing exchange server
.switchIfEmpty(Flux.just(exchange));
Mono<ServerWebExchange> doStuff = processBodyIfExists.flatMap((ServerWebExchange r) -> {
// doStuff here
return Mono.just(r);
})
.next(); // cast to mono
return doStuff;
}
}
答案 2 :(得分:0)
我有类似的问题,我对两者都有依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
和
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
我们必须在 spring-boot-starter-web 中为 tomcat 和 netty 发布排除。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-reactor-netty</artifactId>
</exclusion>
</exclusions>
</dependency>