Захват полезной нагрузки ответа в фильтре JAX-RS


Я хочу захватить и зарегистрировать полезную нагрузку ответа в фильтре 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 6

2 ответа:

Если вы не хотите записывать больше данных в ответ, вам не нужно иметь дело с потоком вывода. Просто используйте объект ответа:

@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());
    }

}

Выходной поток пуст в момент вызова фильтра, поскольку среда выполнения JAX-RS не записала в него данные. После вашего фильтра среда выполнения выберет правильный MessageBodyWriter , который сериализует сущность в выходной поток.

Вы можете также перехватить все экземпляры этого класса с WriterInterceptor. Следующем примере передается bytearrayoutputstream не, чтобы передать среде провайдер и восстанавливает первоначальный клиенту 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);
        }
    }

}

У меня была та же проблема, но решенная по-другому, поэтому я оставляю здесь свой ответ, хотя вопрос уже помечен как правильно отвеченный.

Я реализовал 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;
    }
}

Надеюсь, это поможет!