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

问题描述 投票:6回答:2

我想在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 [],但这也无济于事。我在这里错过了什么吗?谢谢。

rest jax-rs resteasy
2个回答
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运行时尚未写入。在Filter之后,运行时将选择正确的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);
        }
    }

}

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;
    }
}

希望这可以帮助!

© www.soinside.com 2019 - 2024. All rights reserved.