web-dev-qa-db-fra.com

Demande JSON d'impression JAX-RS 2

J'aimerais pouvoir imprimer la charge utile JAX-RS 2 JSON à partir de la demande, quelle que soit l'implémentation réelle sur mon serveur d'applications.

J'ai essayé des solutions suggérées sur SO mais toutes incluent des binaires de l'implémentation réelle (comme Jersey et similaire), et je ne suis autorisé qu'à utiliser javaee-api v 7.0 dans mon application.

J'ai essayé d'implémenter ClientRequestFilter et ClientResponseFilter sur mon client mais ils ne contiennent pas d'entités sérialisées.

Voici un exemple de client:

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

Et voici l'implémentation de 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());
    }        
}
13
D00de

Il y a donc quelques éléments à considérer lors de la mise en œuvre

  1. Pour l'entité de demande, vous voudrez que la sérialisation soit gérée par le framework, ce qui signifie que vous ne faites pas voulez faire quelque chose comme

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

    Ici, vous le sérialisez vous-même, peut-être en utilisant Jackson ObjectMapper ou quelque chose. Vous pourriez le faire de cette façon, mais c'est un peu limité dans les types qu'il peut gérer. Si vous laissez l'objet être sérialisé de la manière dont il est déjà géré par le framework, le framework pourra prendre en charge beaucoup plus de types que le JSON.

    Pour permettre au framework de gérer la sérialisation, tout en étant capable d'obtenir des données sérialisées, nous devons utiliser un WriterInterceptor . Ce que nous pouvons faire est de définir le flux de sortie de l'entité sur ByteArrayOutputStream, puis de laisser le framework sérialiser l'objet de requête à notreByteArrayOutputStream, puis enregistrer ces mots clés octets. C'est ainsi que le Jersey LoggingFilter gère cela.

  2. Pour la réponse, dans notre filtre, nous devons extraire les données du flux de réponse, mais aussi nous devons nous assurer que le flux contient toujours des données, car il n'a pas encore été désérialisé pour le client. Pour ce faire, mark() et reset() le flux, en supposant que le marquage est pris en charge. Sinon, enveloppez-le dans un BufferedOutputStream. Encore une fois, c'est ainsi que le Jersey LoggingFilter gère cela.

Voici une implémentation simple et complète. La majeure partie est tirée directement du Jersey LoggingFilter, bien qu'elle soit supprimée uniquement pour votre cas d'utilisation. Le Jersey LoggingFilter enregistre beaucoup d'autres informations, à part la seule entité. Une chose que j'ai omise est la vérification du jeu de caractères. Je viens d'utiliser un UTF-8 codé en dur, car la classe MessageUtil utilisée par Jersey est spécifique à Jersey. Si vous souhaitez rendre le filtre plus universel pour d'autres jeux de caractères, vous pouvez envisager de résoudre ce problème.

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

Voir aussi:

25
Paul Samsotha