2017-10-05 3 views
6

나는 JAX-RS는 요청 및 응답 정보를 기록 할 필터,이 같은 로그인이 :읽기 JAX-RS 몸의 InputStream 두 번

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() 방법은 InputStream의 본문 내용 읽기를하지만 필요 이 스트림을 다시 설정할 수 없으므로 일부 트릭을 수행하십시오.

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

본문 내용을 읽을 수있는 더 좋은 방법이 있나요 :이 작은 트릭없이 내 나머지 방법은 항상 빈 요청 본문 콘텐츠를 수신?

+0

[link] (https://dennis-xlc.gitbooks.io/restful-java-with-jax-rs-2-0-2rd-edition/en/part1/chapter12)의 인터셉터 및 필터 섹션을 참조하십시오. /reader_and_writer_interceptors.html) – Gautam

답변

1

EDIT 개선 된 버전은 훨씬 강력 해 보이며 JDK 클래스를 사용합니다. 다시 사용하기 전에 close()을 호출하십시오.

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

이것은 markInteger.MAXVALUE 미세 조정에 의해, 상기 버퍼에 전체 스트림을 판독 할 수있다. 이렇게하면 첫 번째 닫기에서 소스가 올바르게 닫혀 OS 리소스가 확보됩니다.


올드 대답

당신이 InputStream 지원 마크 (markSupported())의 실제 구현 확신 할 수 없기 때문에. 첫 번째 apprach에서 입력 스트림 자체를 캐싱하는 것이 좋습니다. exemple 들어

ContainerRequestFilter A의 :

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

} 

이 구현에 순진하다 :

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

캐싱 입력 스트림이 입력 스트림 캐싱에 순진한 접근이다, 당신의 접근 방식과 유사 다양한 방법으로 다음 영역에서 향상시킬 수 있습니다.

  • Chec 캐시 된 입력 스트림을 저장하는 힙을 사용하지 마십시오 원래 스트림
  • markSupported 케이,이
  • 캐시가 동일하게 사용하는 최소한, 현재이 좋은 개선 될 수있다 억제 할 수있는 GC에 압력을 피할 것 http 서버로 바운드.