JAX-RS 2 распечатать запрос JSON

Я хотел бы иметь возможность печатать JAX-RS 2 JSON из запроса независимо от фактической реализации на моем сервере приложений.

Я пытался предложить решения для SO, но все они включают в себя двоичные файлы из реальной реализации (например, Джерси и тому подобное), и мне разрешено использовать только javaee-api v 7.0 в моем приложении.

Я пытался реализовать ClientRequestFilter и ClientResponseFilter на моем клиенте, но они не содержат сериализованные объекты.

Вот пример клиента:

WebTarget target = ClientBuilder.newClient().register(MyLoggingFilter.class).target("http://localhost:8080/loggingtest/resources/accounts");
Account acc = target.request().accept(MediaType.APPLICATION_JSON).get(account.Account.class);

А вот и реализация MyLoggingFilter:

@Provider
public class MyLoggingFilter implements ClientRequestFilter, ClientResponseFilter {

    private static final Logger LOGGER = Logger.getLogger(MyLoggingFilter.class.getName());

    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {

        LOGGER.log(Level.SEVERE, "Request method: {0}", requestContext.getMethod());

    }

    @Override
    public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
        LOGGER.log(Level.SEVERE, "Response status: {0}", responseContext.getStatus());
    }        
}

1 ответ

Решение

Таким образом, есть несколько вещей, которые следует учитывать при попытке реализовать это

  1. Для объекта запроса вы захотите, чтобы сериализация была обработана платформой, то есть вы не хотите делать что-то вроде

    @Override
    public void filter(ClientRequestContext requestContext) {
        Object entity = requestContext.getEntity();
        String serialized = serializeEntity(entity);
        log(serialized);
    

    Здесь вы сериализируете это сами, возможно, используя Джексона ObjectMapper или что-то. Вы можете сделать это таким образом, но это ограничено в типах, которые он может обрабатывать. Если вы позволите объекту сериализоваться так, как он уже обрабатывается фреймворком, фреймворк сможет поддерживать гораздо больше типов, чем просто JSON.

    Чтобы позволить платформе обрабатывать сериализацию и при этом иметь возможность получать сериализованные данные, нам нужно использовать WriterInterceptor, Что мы можем сделать, это установить выходной поток объекта в ByteArrayOutputStream и затем позвольте платформе сериализовать объект запроса в наш ByteArrayOutputStream затем послесловия записывают эти байты. Это как Джерси LoggingFilter обрабатывает это.

  2. Для ответа в нашем фильтре нам нужно извлечь данные из потока ответов, но мы также должны убедиться, что в потоке все еще есть данные, так как они еще не были десериализованы для клиента. Сделать это mark() а также reset() поток, предполагая, что маркировка поддерживается. Если нет, заверните его в BufferedOutputStream, Опять же, это как Джерси LoggingFilter обрабатывает это.

Ниже приведена полная простая реализация. Большая часть этого взята прямо из Джерси LoggingFilter, хотя это урезано только для вашего случая использования. Джерси LoggingFilter регистрирует много другой информации, кроме самой сущности. Одна вещь, которую я оставил, это проверка кодировки. Я просто использовал жесткий код UTF-8, так как MessageUtil класс, используемый Джерси, является специфическим для Джерси. Если вы хотите сделать фильтр более универсальным для других кодировок, возможно, вы захотите исправить это.

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.logging.Logger;
import javax.annotation.Priority;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;

@Priority(Integer.MIN_VALUE)
public class EntityLoggingFilter implements ClientRequestFilter, ClientResponseFilter, WriterInterceptor {

    private static final Logger logger = Logger.getLogger(EntityLoggingFilter.class.getName());
    private static final String ENTITY_STREAM_PROPERTY = "EntityLoggingFilter.entityStream";
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
    private final int maxEntitySize = 1024 * 8;

    private void log(StringBuilder sb) {
        logger.info(sb.toString());
    }

    private InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Charset charset) throws IOException {
        if (!stream.markSupported()) {
            stream = new BufferedInputStream(stream);
        }
        stream.mark(maxEntitySize + 1);
        final byte[] entity = new byte[maxEntitySize + 1];
        final int entitySize = stream.read(entity);
        b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset));
        if (entitySize > maxEntitySize) {
            b.append("...more...");
        }
        b.append('\n');
        stream.reset();
        return stream;
    }

    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {
        if (requestContext.hasEntity()) {
            final OutputStream stream = new LoggingStream(requestContext.getEntityStream());
            requestContext.setEntityStream(stream);
            requestContext.setProperty(ENTITY_STREAM_PROPERTY, stream);
        }
    }

    @Override
    public void filter(ClientRequestContext requestContext,
            ClientResponseContext responseContext) throws IOException {
        final StringBuilder sb = new StringBuilder();
        if (responseContext.hasEntity()) {
            responseContext.setEntityStream(logInboundEntity(sb, responseContext.getEntityStream(),
                    DEFAULT_CHARSET));
            log(sb);
        }

    }

    @Override
    public void aroundWriteTo(WriterInterceptorContext context)
            throws IOException, WebApplicationException {
        final LoggingStream stream = (LoggingStream) context.getProperty(ENTITY_STREAM_PROPERTY);
        context.proceed();
        if (stream != null) {
            log(stream.getStringBuilder(DEFAULT_CHARSET));
        }
    }

    private class LoggingStream extends FilterOutputStream {

        private final StringBuilder sb = new StringBuilder();
        private final ByteArrayOutputStream baos = new ByteArrayOutputStream();

        LoggingStream(OutputStream out) {
            super(out);
        }

        StringBuilder getStringBuilder(Charset charset) {
            // write entity to the builder
            final byte[] entity = baos.toByteArray();

            sb.append(new String(entity, 0, entity.length, charset));
            if (entity.length > maxEntitySize) {
                sb.append("...more...");
            }
            sb.append('\n');

            return sb;
        }

        @Override
        public void write(final int i) throws IOException {
            if (baos.size() <= maxEntitySize) {
                baos.write(i);
            }
            out.write(i);
        }
    }
}

Смотрите также:

Другие вопросы по тегам