2016-04-17 20 views
10

Vorrei poter stampare il carico utile JSON JSON-RS 2 JSON dalla richiesta, indipendentemente dall'attuale implementazione sul mio server applicazioni.JAX-RS 2 print Richiesta JSON

Ho provato le soluzioni suggerite su SO ma tutte includono i binari dall'implementazione reale (come Jersey e simili) e sono autorizzato solo a utilizzare javaee-api v 7.0 nella mia applicazione.

Ho provato a implementare ClientRequestFilter e ClientResponseFilter sul mio Client ma non contengono entità serializzate.

Ecco un esempio di 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); 

Ed ecco l'implementazione di 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()); 
    }   
} 

risposta

15

quindi ci sono un paio di cose da considerare quando si cerca di attuare questa

  1. Per l'entità richiesta, è necessario che la serializzazione sia gestita dal framework, meanin g si non vuole fare qualcosa di simile

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

    Qui si sta serializzazione da soli, magari usando Jackson ObjectMapper o qualcosa del genere. Tu potresti farlo in questo modo, ma è un po 'limitato nei tipi che può gestire. Se lasci che l'oggetto sia serializzato nel modo in cui è già gestito dal framework, il framework sarà in grado di supportare molti più tipi di solo JSON.

    Per consentire al framework di gestire la serializzazione ed essere ancora in grado di ottenere dati serializzati, è necessario utilizzare uno WriterInterceptor. Quello che possiamo fare è impostare il flusso di output dell'entità su ByteArrayOutputStream, quindi lasciare che il framework serializzi l'oggetto richiesta su il nostroByteArrayOutputStream, quindi le password registrano quei byte. Questo è come la Jersey LoggingFilter gestisce questo.

  2. Per la risposta, nel nostro filtro, abbiamo bisogno di estrarre i dati dal flusso di risposta, ma anche abbiamo bisogno di assicurarsi che il flusso ha ancora dei dati, in quanto non è stato ancora deserializzati per il cliente. Per fare questo mark() e reset() lo stream, supponendo che il contrassegno sia supportato. In caso contrario, avvolgere in un BufferedOutputStream. Ancora una volta, questo è come la Jersey LoggingFilter gestisce questo.

Di seguito è una completa implementazione completa. La maggior parte viene presa direttamente dal Jersey LoggingFilter, sebbene sia ridotta solo per il tuo caso d'uso. Il Jersey LoggingFilter registra molte altre informazioni, oltre alla giusta entità. Una cosa che ho tralasciato è il controllo del charset. Ho appena usato un UTF-8 con codifica rigida, poiché la classe MessageUtil utilizzata da Jersey è specifica di Jersey. Se si desidera rendere il filtro più universale per altri set di caratteri, è possibile che si cerchi di risolverlo.

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

Vedere anche:

+0

funziona alla grande. Ho solo bisogno di annotare il filtro come @Provider e registrarlo sul mio Client e registra JSON da POSTed Entity. BTW, sto usando JAX-B per serializzare Entities. Grazie per il tuo tempo, bel padre, ho il mio upvote e l'accettazione. – D00de

+0

Ottimo per il debugging! –