在JAX-RS过滤器中捕获响应有效负载

时间:2014-08-15 18:32:48

标签: rest jax-rs resteasy

我想在JAX-RS过滤器中捕获并记录响应有效负载。这是我用来拦截响应的过滤器方法的代码片段。 (仅供参考 - 我正在使用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 [],但这也无济于事。我在这里错过了什么吗?感谢。

2 个答案:

答案 0 :(得分:19)

如果您不想在回复中写入更多数据,则无需处理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);
        }
    }

}

答案 1 :(得分:2)

我遇到了同样的问题并且解决了不同的问题,所以我也留下了我的回复,尽管这个问题已经标记为正确答案了。

我实施了ContainerResponseFilter并注入了Providers,我通过该MessageBodyWriter检索了响应的特定实体和特定MediaType的{​​{1}};然后我用它将实体写入一个可访问的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;
    }
}

希望这有帮助!