Spring中操作日志记录web请求的body报文

在spring中,通常可以使用切面编程方式对web请求记录操作日志。但是这种方式存在一个问题,那就是只能记录url中的请求参数,无法记录POST或者PUT请求的报文体,因为报文体是放在request对象的InputStream中的,只能读取一次。解决方法就是利用HttpServletRequestWrapper先读取InputStream,记录到一个头参数中,然后再重新放到InputStream中去。

代码如下:
先创建一个WrappedHttpServletRequest类:

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.*;

public class WrappedHttpServletRequest extends HttpServletRequestWrapper {

  private byte[] bytes;
  private WrappedServletInputStream wrappedServletInputStream;

  public WrappedHttpServletRequest(HttpServletRequest request) throws IOException {
    super(request);
    // 读取输入流里的请求参数,并保存到bytes里
    bytes = IOUtils.toByteArray(request.getInputStream());
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
    this.wrappedServletInputStream = new WrappedServletInputStream(byteArrayInputStream);

    // 很重要,把post参数重新写入请求流
    reWriteInputStream();
  }

  /**
   * 把参数重新写进请求里
   */
  public void reWriteInputStream() {
    wrappedServletInputStream
        .setStream(new ByteArrayInputStream(bytes != null ? bytes : new byte[0]));
  }

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

  @Override
  public BufferedReader getReader() throws IOException {
    return new BufferedReader(new InputStreamReader(wrappedServletInputStream));
  }

  /**
   * 获取post参数,可以自己再转为相应格式
   */
  public String getRequestParams() throws IOException {
    return new String(bytes, this.getCharacterEncoding());
  }

  private class WrappedServletInputStream extends ServletInputStream {

    public void setStream(InputStream stream) {
      this.stream = stream;
    }

    private InputStream stream;

    public WrappedServletInputStream(InputStream stream) {
      this.stream = stream;
    }

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

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

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

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

再创建一个LogFilter对象:

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Component
@WebFilter(value = "/*", filterName = "logFilter")
@Slf4j
public class LogFilter implements Filter {

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {}

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {

    try {
      WrappedHttpServletRequest requestWrapper =
          new WrappedHttpServletRequest((HttpServletRequest) request);

      // 获取请求参数
      String requestBody = requestWrapper.getRequestParams();
      if (!StringUtils.isBlank(requestBody)) {
        if (requestBody.length() >= 8192) {
          requestBody = requestBody.substring(0, 8192);
        }
        request.setAttribute("request-body", requestBody); // 这里创建一个参数头,把要记录的报文放到参数头里面,在切面中读取这个参数头
      }

      // 这里doFilter传入我们实现的子类
      chain.doFilter(requestWrapper, response);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
  }

  @Override
  public void destroy() {}
}
原文地址:https://www.cnblogs.com/lasdaybg/p/9929501.html