记录 HttpRequest 参数和请求正文

发布于 2024-11-14 20:29:01 字数 377 浏览 1 评论 0原文

我正在尝试为我的网络应用程序创建请求日志。我正在使用Spring 3。 0.

我实现了一个扩展 HandlerInterceptorAdapter 的类,并使用 preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 来拦截请求。

在该方法中,我希望能够记录请求正文(我的参数是直接写入请求正文的 XML 中的对象),为此我使用 request.getReader();

问题是 - 稍后当 spring 控制器尝试读取请求时,我将收到一个 IllegalStateException

有办法做我想做的事吗?

I am trying to create a request log for my web app. I am using Spring 3.
0.

I implemented a class extending HandlerInterceptorAdapter and used the preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) to intercept the request.

In the method i want to be able to log the request body (my parameters are objects in XML that are written directly to the request body), and for that i use request.getReader();

The problem is - later on I will get an IllegalStateException when the spring controller tries to read the request.

Is there a way to do what I intend?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

幻想少年梦 2024-11-21 20:29:01

您可以使用过滤器来做到这一点。请求参数很容易处理。
然而处理请求体会困难得多
并且需要包装 servlet 请求,请参阅: HttpServletRequest

您需要查看传入请求有多大,并决定是否要将请求正文存储为 tmp 文件或字符串。

您将需要使用您的文件或用于日志记录的已保存字符串覆盖 ServetRequest.getInputStream()。

如果请求正文很大,我建议将输入流放入缓冲输入流中,然后读取正文的开头。

public class LogRequest extends HttpServletRequestWrapper {

    public LogRequest(HttpServletRequest request) {
        super(request);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        //read from tmp file or string.
    }

    @Override
    public BufferedReader getReader() throws IOException {
        //read from tmp file or string
    }

}

You can do this with a filter. The request parameters are easy to handle.
However dealing with the request body will be much more difficult
and will require wrapping the servlet request see: HttpServletRequest.

You will need to look how big the incoming request is and decide whether you want to store the request body as a tmp file or string.

You will need to override ServetRequest.getInputStream() with your file or saved string that used for logging.

If the request body is huge I recommend putting the input stream into a buffered input stream and then reading the start of the body.

public class LogRequest extends HttpServletRequestWrapper {

    public LogRequest(HttpServletRequest request) {
        super(request);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        //read from tmp file or string.
    }

    @Override
    public BufferedReader getReader() throws IOException {
        //read from tmp file or string
    }

}
壹場煙雨 2024-11-21 20:29:01

Spring 有一个现成的过滤器可以为您执行此操作 - 请参阅 这个答案

请注意,使用此解决方案时,仅在请求处理完成并且应用程序已读取请求正文后才会记录请求正文。

Spring has a ready filter to do that for you - see usage of AbstractRequestLoggingFilter and its subclasses described in this answer.

Be aware that when using this solution, the request body will be logged only after the request processing is complete and the body has been read by your application.

清晰传感 2024-11-21 20:29:01

小请求的简单实现。不要将其用于多部分请求。

package ru.rbs.logger.web;

import org.apache.commons.io.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

class CachedRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] cachedBody;

    CachedRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        IOUtils.copy(request.getInputStream(), bos);
        cachedBody = bos.toByteArray();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new CachedServletInputStream();
    }

    byte[] toByteArray(){
        return cachedBody;
    }

    private class CachedServletInputStream extends ServletInputStream {
        private InputStream baseInputStream;

        CachedServletInputStream() throws IOException {
            baseInputStream = new ByteArrayInputStream(cachedBody);
        }

        @Override
        public boolean isFinished() {
            return false;
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setReadListener(ReadListener readListener) {

        }

        @Override
        public int read() throws IOException {
            return baseInputStream.read();
        }
    }
}

Simple implementation for small requests. Don't use it for multipart request.

package ru.rbs.logger.web;

import org.apache.commons.io.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

class CachedRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] cachedBody;

    CachedRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        IOUtils.copy(request.getInputStream(), bos);
        cachedBody = bos.toByteArray();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new CachedServletInputStream();
    }

    byte[] toByteArray(){
        return cachedBody;
    }

    private class CachedServletInputStream extends ServletInputStream {
        private InputStream baseInputStream;

        CachedServletInputStream() throws IOException {
            baseInputStream = new ByteArrayInputStream(cachedBody);
        }

        @Override
        public boolean isFinished() {
            return false;
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setReadListener(ReadListener readListener) {

        }

        @Override
        public int read() throws IOException {
            return baseInputStream.read();
        }
    }
}
ペ泪落弦音 2024-11-21 20:29:01

使用 TeeInputStream。这会同时写入两个流:源输入流和一些输出流。只需重写请求包装器中的 ServletInputStream 并从 TeeInputStream 读取即可。然后,您可以在调用chain.doFilter()后从输出流中读取:

@Override
public void doFilterNonOption(HttpServletRequest request, HttpServletResponse response,
      FilterChain chain) throws IOException, ServletException {
  ByteArrayOutputStream requestBaos = new ByteArrayOutputStream();
  final PrintStream requestPrintStream = new PrintStream(requestBaos);
  final TeeInputStream inputStream = new TeeInputStream(request.getInputStream(), requestPrintStream);

  HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(request) {
    @Override
    public ServletInputStream getInputStream() {
      return new ServletInputStream() {
        @Override
        public boolean isFinished() {
          return false;
        }

        @Override
        public boolean isReady() {
          return true;
        }

        @Override
        public void setReadListener(ReadListener readListener) {
          // NOOP
        }

        @Override
        public int read() throws IOException {
          return inputStream.read();
        }
      };
    }
  };

  try {
    chain.doFilter(wrappedRequest, response);
  } finally {
    System.out.println(requestBaos.toString(StandardCharsets.UTF-8));
  }
}

This can be accomplished fairly easily using TeeInputStream. This writes to two streams at once: the source input stream, and some output stream. Simply override the ServletInputStream in the request wrapper, and read from the TeeInputStream. You can then read from the output stream after calling chain.doFilter():

@Override
public void doFilterNonOption(HttpServletRequest request, HttpServletResponse response,
      FilterChain chain) throws IOException, ServletException {
  ByteArrayOutputStream requestBaos = new ByteArrayOutputStream();
  final PrintStream requestPrintStream = new PrintStream(requestBaos);
  final TeeInputStream inputStream = new TeeInputStream(request.getInputStream(), requestPrintStream);

  HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(request) {
    @Override
    public ServletInputStream getInputStream() {
      return new ServletInputStream() {
        @Override
        public boolean isFinished() {
          return false;
        }

        @Override
        public boolean isReady() {
          return true;
        }

        @Override
        public void setReadListener(ReadListener readListener) {
          // NOOP
        }

        @Override
        public int read() throws IOException {
          return inputStream.read();
        }
      };
    }
  };

  try {
    chain.doFilter(wrappedRequest, response);
  } finally {
    System.out.println(requestBaos.toString(StandardCharsets.UTF-8));
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文