WebFlux应用程序中的WebFilter

时间:2017-10-24 22:11:51

标签: spring spring-boot spring-webflux

我有一个使用Spring Boot 2.0.0.M5 / 2.0.0.BUILD-SNAPSHOT的Spring Boot WebFlux应用程序。 我需要在所有日志中添加trace-id。

为了让它在WebFlux应用程序中运行,我尝试使用描述为herehere的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是在启动时加载的。 还有什么需要让过滤器工作吗?

3 个答案:

答案 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>