java – 读取JAX-RS主体InputStream两次

前端之家收集整理的这篇文章主要介绍了java – 读取JAX-RS主体InputStream两次前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个JAX-RS日志记录过滤器来记录请求和响应详细信息,如下所示:
  1. public class LoggingFilter implements ContainerRequestFilter,ContainerResponseFilter {
  2. @Override
  3. public void filter(final ContainerRequestContext requestContext) throws IOException {
  4. ...
  5. String body = getBody(request);
  6. ...
  7. if (LOGGER.isDebugEnabled()) {
  8. LOGGER.debug("request: {}",httpRequest);
  9. }
  10. }
  11. }

getBody()方法从InputStream中读取正文内容,但我需要做一些技巧,因为我无法重置此流.没有这个小技巧我的休息方法总是收到空的请求正文内容

  1. private String getBody(final ContainerRequestContext requestContext) {
  2. try {
  3. byte[] body = IoUtils.toByteArray(requestContext.getEntityStream());
  4.  
  5. InputStream stream = new ByteArrayInputStream(body);
  6. requestContext.setEntityStream(stream);
  7.  
  8. return new String(body);
  9. } catch (IOException e) {
  10. return null;
  11. }
  12. }

有没有更好的方法来阅读身体内容

解决方法

编辑这是一个改进的版本,看起来更强大,并使用JDK类.只需在重用之前调用close().
  1. public class CachingInputStream extends BufferedInputStream {
  2. public CachingInputStream(InputStream source) {
  3. super(new PostCloseProtection(source));
  4. super.mark(Integer.MAX_VALUE);
  5. }
  6.  
  7. @Override
  8. public synchronized void close() throws IOException {
  9. if (!((PostCloseProtection) in).decoratedClosed) {
  10. in.close();
  11. }
  12. super.reset();
  13. }
  14.  
  15. private static class PostCloseProtection extends InputStream {
  16. private volatile boolean decoratedClosed = false;
  17. private final InputStream source;
  18.  
  19. public PostCloseProtection(InputStream source) {
  20. this.source = source;
  21. }
  22.  
  23. @Override
  24. public int read() throws IOException {
  25. return decoratedClosed ? -1 : source.read();
  26. }
  27.  
  28. @Override
  29. public int read(byte[] b) throws IOException {
  30. return decoratedClosed ? -1 : source.read(b);
  31. }
  32.  
  33. @Override
  34. public int read(byte[] b,int off,int len) throws IOException {
  35. return decoratedClosed ? -1 : source.read(b,off,len);
  36. }
  37.  
  38. @Override
  39. public long skip(long n) throws IOException {
  40. return decoratedClosed ? 0 : source.skip(n);
  41. }
  42.  
  43. @Override
  44. public int available() throws IOException {
  45. return source.available();
  46. }
  47.  
  48. @Override
  49. public void close() throws IOException {
  50. decoratedClosed = true;
  51. source.close();
  52. }
  53.  
  54. @Override
  55. public void mark(int readLimit) {
  56. source.mark(readLimit);
  57. }
  58.  
  59. @Override
  60. public void reset() throws IOException {
  61. source.reset();
  62. }
  63.  
  64. @Override
  65. public boolean markSupported() {
  66. return source.markSupported();
  67. }
  68. }
  69. }

这允许通过将标记调整为Integer.MAXVALUE来读取缓冲区中的整个流.这也确保在第一次关闭以释放OS资源时正确关闭源.

老答案

因为您无法确定InputStream支持标记的实际实现(markSupported()).你最好在第一个apprach中缓存输入流本身.

例如,在ContainerRequestFilter中:

  1. @Component
  2. @Provider
  3. @PreMatching
  4. @Priority(1)
  5. public class ReadSomethingInPayloadFilter implements ContainerRequestFilter {
  6.  
  7. @Override
  8. public void filter(ContainerRequestContext request) throws IOException {
  9. CachingInputStream entityStream = new CachingInputStream(request.getEntityStream());
  10.  
  11. readPayload(entityStream);
  12.  
  13. request.setEntityStream(entityStream.getCachedInputStream());
  14. }
  15. }

缓存输入流是一种简单的输入流缓存方法,它与您的方法类似:

  1. class CachingInputStream extends InputStream {
  2. public static final int END_STREAM = -1;
  3. private final InputStream is;
  4. private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
  5.  
  6. public CachingInputStream(InputStream is) {
  7. this.is = is;
  8. }
  9.  
  10. public InputStream getCachedInputStream() {
  11. return new ByteArrayInputStream(baos.toByteArray());
  12. }
  13.  
  14. @Override
  15. public int read() throws IOException {
  16. int result = is.read();
  17. // Avoid rewriting the end char (-1) otherwise it will be considered as a real char.
  18. if (result != END_STREAM)
  19. baos.write(result);
  20. return result;
  21. }
  22.  
  23. @Override
  24. public int available() throws IOException {
  25. return is.available();
  26. }
  27.  
  28. @Override
  29. public void close() throws IOException {
  30. is.close();
  31. }
  32.  
  33. }

这种实现方式在各方面都很幼稚,可以在以下方面进行改进,可能更多:

>检查原始流上的markSupported>不要使用堆来存储缓存的输入流,这样可以避免对GC施加压力>缓存是无限的,目前这可能是一个很好的改进,至少使用与http服务器相同的边界.

猜你在找的Java相关文章