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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章