【问题标题】:Copy of the request/response body on a Spring reactive app?Spring 反应式应用程序上的请求/响应正文的副本?
【发布时间】:2018-04-21 07:58:29
【问题描述】:

我正在研究访问 HTTP 请求和响应主体以在 Spring 反应式应用程序中进行跟踪的最佳方法。

对于以前的版本,我们利用 Servlet 过滤器和 Servlet 请求包装器来使用传入请求的输入流并保存它的副本以异步处理跟踪(我们将它们发送到 Elasticsearch)。

但是对于 Spring 响应式应用程序(使用 webflux),我想知道在解码之前访问请求的最合适方法是什么。有什么想法吗?

【问题讨论】:

    标签: java spring-webflux


    【解决方案1】:

    事实证明,这可以使用提供的装饰器来实现:分别为ServerWebExchangeDecoratorServerHttpRequestDecoratorServerHttpResponseDecorator

    这是一个示例请求装饰器,它累积 DataBuffer 内容作为请求的默认订阅者读取的内容:

    @Slf4j
    public class CachingServerHttpRequestDecorator extends ServerHttpRequestDecorator {
    
        @Getter
        private final OffsetDateTime timestamp = OffsetDateTime.now();
        private final StringBuilder cachedBody = new StringBuilder();
    
        CachingServerHttpRequestDecorator(ServerHttpRequest delegate) {
            super(delegate);
        }
    
        @Override
        public Flux<DataBuffer> getBody() {
            return super.getBody().doOnNext(this::cache);
        }
    
        @SneakyThrows
        private void cache(DataBuffer buffer) {
            cachedBody.append(UTF_8.decode(buffer.asByteBuffer())
             .toString());
        }
    
        public String getCachedBody() {
            return cachedBody.toString();
        }
    

    只要确保,当您装饰由WebFilter 传递的ServerWebExchange 时,您也覆盖getRequest() 以返回请求装饰器:

    public final class PartnerServerWebExchangeDecorator extends ServerWebExchangeDecorator {
    
        private final ServerHttpRequestDecorator requestDecorator;
        private final ServerHttpResponseDecorator responseDecorator;
    
        public PartnerServerWebExchangeDecorator(ServerWebExchange delegate) {
            super(delegate);
            this.requestDecorator = new PartnerServerHttpRequestDecorator(delegate.getRequest());
            this.responseDecorator = new PartnerServerHttpResponseDecorator(delegate.getResponse());
        }
    
        @Override
        public ServerHttpRequest getRequest() {
            return requestDecorator;
        }
    
        @Override
        public ServerHttpResponse getResponse() {
            return responseDecorator;
        }
    
    }
    

    在过滤器上:

    @Component
    public class TracingFilter implements WebFilter {
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
            return chain.filter(new PartnerServerWebExchangeDecorator(exchange));
        }
    }
    

    可以这样使用(注意静态导入的函数):

    @Bean
    public HttpHandler myRoute(MyHandler handler) {
        final RouterFunction<ServerResponse> routerFunction =
            route(POST("/myResource"), handler::persistNotification);
        return webHandler(toWebHandler(routerFunction))
            .filter(new TracingFilter())
            .build();
    }
    

    【讨论】:

    • 您能否在 Spring Boot 2 的 WebFilter 中举例说明如何执行此操作?无法正确复制请求正文,因此在读取正文时,有效负载被耗尽并且请求失败
    • 我能够使它与上面的装饰器一起工作。我会在上班时仔细检查,如果发现任何差异,我会发布。
    • 我已经用我们生产服务中装饰器使用的确切代码更新了答案。
    • @javabeats 感谢您与我们分享这段出色的代码。你介意分享一下,你是怎么想出这个解决方案的?我们花了很长时间寻找在请求映射读取请求正文之前尚未消耗请求正文的可能性。
    • 我记得 Spring MVC 曾经为 ServletRequest/Response 对象提供装饰器,并且很好奇是否通过 webflux 提供了类似的东西。浏览代码我发现了这些,但当时几乎没有关于它们的文档。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-20
    • 2019-04-15
    • 2018-07-11
    • 1970-01-01
    • 2017-03-04
    • 2018-08-28
    • 2021-06-12
    相关资源
    最近更新 更多