2017-10-05 94 views
6

Ben JAX-RS istek ve yanıt ayrıntılarını günlüğe filtre, böyle bir şey günlüğü vardır:Oku JAX-RS vücut inputStream iki kez

public class LoggingFilter implements ContainerRequestFilter, ContainerResponseFilter { 
    @Override 
    public void filter(final ContainerRequestContext requestContext) throws IOException { 
     ... 
     String body = getBody(request);   
     ... 
     if (LOGGER.isDebugEnabled()) { 
      LOGGER.debug("request: {}", httpRequest); 
     } 
    } 
} 

getBody() yöntem InputStream vücut içeriği okur ama gerek Bazı hile yapmak çünkü bu akışı sıfırlayamıyorum.

private String getBody(final ContainerRequestContext requestContext) { 
    try { 
     byte[] body = IOUtils.toByteArray(requestContext.getEntityStream()); 

     InputStream stream = new ByteArrayInputStream(body); 
     requestContext.setEntityStream(stream); 

     return new String(body); 
    } catch (IOException e) { 
     return null; 
    } 
} 

vücut içeriğini okumak için daha iyi bir yolu var mı: Bu küçük hile olmadan benim dinlenme yöntemler her zaman boş istek gövdesi içeriği almak?

+0

Bkz. [Bağlantı] bölümündeki Kapsüller ve Filtreler bölümü (https://dennis-xlc.gitbooks.io/restful-java-with-jax-rs-2-0-2rd-edition/en/part1/chapter12 /reader_and_writer_interceptors.html) – Gautam

cevap

1

EDIT Daha güçlü ve JDK sınıflarını kullanan geliştirilmiş bir sürümü. Yeniden kullanmadan önce sadece close()'u çağırın.

public class CachingInputStream extends BufferedInputStream {  
    public CachingInputStream(InputStream source) { 
     super(new PostCloseProtection(source)); 
     super.mark(Integer.MAX_VALUE); 
    } 

    @Override 
    public synchronized void close() throws IOException { 
     if (!((PostCloseProtection) in).decoratedClosed) { 
      in.close(); 
     } 
     super.reset(); 
    } 

    private static class PostCloseProtection extends InputStream { 
     private volatile boolean decoratedClosed = false; 
     private final InputStream source; 

     public PostCloseProtection(InputStream source) { 
      this.source = source; 
     } 

     @Override 
     public int read() throws IOException { 
      return decoratedClosed ? -1 : source.read(); 
     } 

     @Override 
     public int read(byte[] b) throws IOException { 
      return decoratedClosed ? -1 : source.read(b); 
     } 

     @Override 
     public int read(byte[] b, int off, int len) throws IOException { 
      return decoratedClosed ? -1 : source.read(b, off, len); 
     } 

     @Override 
     public long skip(long n) throws IOException { 
      return decoratedClosed ? 0 : source.skip(n); 
     } 

     @Override 
     public int available() throws IOException { 
      return source.available(); 
     } 

     @Override 
     public void close() throws IOException { 
      decoratedClosed = true; 
      source.close(); 
     } 

     @Override 
     public void mark(int readLimit) { 
      source.mark(readLimit); 
     } 

     @Override 
     public void reset() throws IOException { 
      source.reset(); 
     } 

     @Override 
     public boolean markSupported() { 
      return source.markSupported(); 
     } 
    } 
} 

Bu markInteger.MAXVALUE için değişiklikler yaparak, tampon içinde tüm akışı okumayı sağlar. Bu aynı zamanda, kaynak işletim sisteminin OS kaynağını serbest bırakmak için ilk kapanışta düzgün şekilde kapatıldığından emin olur.


Eski Cevap

Eğer InputStream destek işareti (markSupported()) gerçek uygulaması emin olamaz gibi. Giriş akışının kendisini ilk uygulamada önbelleğe almak daha iyidir. Exemple için

ContainerRequestFilter bir de:

class CachingInputStream extends InputStream { 
    public static final int END_STREAM = -1; 
    private final InputStream is; 
    private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 

    public CachingInputStream(InputStream is) { 
     this.is = is; 
    } 

    public InputStream getCachedInputStream() { 
     return new ByteArrayInputStream(baos.toByteArray()); 
    } 

    @Override 
    public int read() throws IOException { 
     int result = is.read(); 
     // Avoid rewriting the end char (-1) otherwise it will be considered as a real char. 
     if (result != END_STREAM) 
      baos.write(result); 
     return result; 
    } 

    @Override 
    public int available() throws IOException { 
     return is.available(); 
    } 

    @Override 
    public void close() throws IOException { 
     is.close(); 
    } 

} 

Bu uygulama içinde naif geçerli:

@Component 
@Provider 
@PreMatching 
@Priority(1) 
public class ReadSomethingInPayloadFilter implements ContainerRequestFilter { 

    @Override 
    public void filter(ContainerRequestContext request) throws IOException { 
     CachingInputStream entityStream = new CachingInputStream(request.getEntityStream()); 

     readPayload(entityStream); 

     request.setEntityStream(entityStream.getCachedInputStream()); 
    } 
} 

önbelleğe alma giriş akışı giriş akışı önbelleğe alma saf bir yaklaşımdır, bu yaklaşıma yolu benzerdir çeşitli yollarla, aşağıdaki alanda ve muhtemelen daha fazla geliştirilebilir:

  • Chec önbelleğe giriş akışı depolamak için yığın kullanmayın orijinal akışı
  • üzerine markSupported k, bu
  • Önbellek aynı kullanmayın en azından şu anda bu iyi bir gelişme olabilir sınırsız olduğu GC üzerindeki baskıyı önleyeceğini http sunucunuz olarak sınırlar.