【问题标题】:Capture Response Payload in JAX-RS filter在 JAX-RS 过滤器中捕获响应负载
【发布时间】:2014-08-15 18:32:47
【问题描述】:

我想在 JAX-RS 过滤器中捕获并记录响应负载。这是我用来拦截响应的过滤器方法的代码 sn-p。 (仅供参考 - 我正在使用 RestEasy 进行实施)

@Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext         responseContext) throws IOException {
    ...
      final OutputStream out = responseContext.getEntityStream();
       try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
               out.write(baos.toByteArray());
        ....
   }
}

但是,ByteArrayOutputStream 结果是空的。查看 RestEasy 代码,它使用 DeferredOutputStream,但不确定在拉取响应有效负载方面有何影响。我曾尝试直接写入 byte[] ,但这也无济于事。我在这里错过了什么吗?谢谢。

【问题讨论】:

    标签: rest jax-rs resteasy


    【解决方案1】:

    如果您不想向响应中写入更多数据,则无需处理 OutputStream。只需使用响应实体:

    @Provider
    public class SomeFilter implements ContainerResponseFilter {
    
        private Logger LOG = LoggerFactory.getLogger(SomeFilter.class);
    
        @Override
        public void filter(ContainerRequestContext requestContext,
                ContainerResponseContext responseContext) throws IOException {
            LOG.info("response entity: " + responseContext.getEntity());
        }
    
    }
    

    在调用 Filter 时,OutputStream 为空,因为 JAX-RS 运行时尚未写入。在您的过滤器之后,运行时将选择正确的MessageBodyWriter,它将实体序列化到 OutputStream。

    您还可以使用WriterInterceptor 拦截所有 MessageBodyWriters。以下示例将 ByteArrayOutputStream 传递给 MessageBodyWriter 并在之后恢复原始 OutputStream:

    @Provider
    public class ResponseInterceptor implements WriterInterceptor {
    
        private Logger LOG = LoggerFactory.getLogger(ResponseInterceptor.class);
    
        @Override
        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
            OutputStream originalStream = context.getOutputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            context.setOutputStream(baos);
            try {
                context.proceed();
            } finally {
                LOG.info("response body: " + baos.toString("UTF-8"));
                baos.writeTo(originalStream);
                baos.close();
                context.setOutputStream(originalStream);
            }
        }
    
    }
    

    【讨论】:

    • 我需要 OutputStream 将响应负载记录为字符串。 responseContext.getEntity() 的使用将返回响应实体/对象,如果它没有实现 toString 方法,这将没有用。
    • 你可以使用Jackson的ObjectMapper来序列化对象。
    • 为什么 "baos.toString("UTF-8")" 只有在执行 context.proceed() 后才有效?如果在它之前调用它,它会返回一个空字符串。
    • 该方法被称为aroundWriteTo。您可以拦截写入响应的过程。 context.proceed 触发写入。
    【解决方案2】:

    我遇到了同样的问题,但解决的方式不同,所以我也把我的回答留在这里,尽管问题已经被标记为正确回答。

    我实现了一个ContainerResponseFilter 并注入了Providers,通过它我检索了响应的特定实体的MessageBodyWriter 和特定的MediaType;然后我用它把实体写入一个可访问的OutputStream,我用它来记录实体。

    此方法允许您捕获响应的确切负载,而不仅仅是附加到 Response 的实体,即如果实体将被序列化为 JSON,那么您将记录 JSON,如果它被序列化作为 XML,您将记录 XML。如果使用附加实体的toString()方法就足够了,那么这种方法只是无用的计算成本。

    这是代码(技巧在函数CustomResponseLogger.payloadMessage中完成):

    @Provider
    public class CustomResponseLogger implements ContainerResponseFilter {
        private static final Logger LOGGER = LoggerFactory.getLogger(CustomResponseLogger.class);
    
        @Context private Providers providers;
    
        @Override
        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
            String message = new String("Outgoing message").concat(System.lineSeparator());
            if (responseContext.getMediaType() != null) 
                message = message.concat("Content-Type: ").concat(responseContext.getMediaType().toString()).concat(System.lineSeparator());
            message = message.concat("Payload: ").concat(payloadMessage(responseContext)).concat(System.lineSeparator());
            LOGGER.info(message);
        }
    
        private String payloadMessage(ContainerResponseContext responseContext) throws IOException {
            String message = new String();
            if (responseContext.hasEntity()) {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();           
                Class<?> entityClass = responseContext.getEntityClass();
                Type entityType = responseContext.getEntityType();
                Annotation[] entityAnnotations = responseContext.getEntityAnnotations();
                MediaType mediaType = responseContext.getMediaType();
                @SuppressWarnings("unchecked")
                MessageBodyWriter<Object> bodyWriter = (MessageBodyWriter<Object>) providers.getMessageBodyWriter(entityClass, 
                        entityType, 
                        entityAnnotations, 
                        mediaType); // I retrieve the bodywriter
                bodyWriter.writeTo(responseContext.getEntity(), 
                        entityClass, 
                        entityType, 
                        entityAnnotations, 
                        mediaType, 
                        responseContext.getHeaders(), 
                        baos); // I use the bodywriter to write to an accessible outputStream
                message = message.concat(new String(baos.toByteArray())); // I convert the stream to a String
            }
            return message;
        }
    }
    

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 2015-04-26
      • 2021-06-09
      • 2016-07-23
      • 2013-07-26
      • 2015-07-06
      • 1970-01-01
      • 1970-01-01
      • 2020-07-21
      • 1970-01-01
      相关资源
      最近更新 更多