spring boot過濾器中獲取Url請求中的參數(打印接口參數日誌)

最近有一個需要從接口請求參數後,打印日誌,進行接口參數記錄。

這裏記錄一下處理過程中出現的問題。
首先想到的就是request.getParameter(String )方法,但是這個方法只能在get請求中取到參數,post是不行的,後來想到了使用流的方式,調用request.getInputStream()獲取流,然後從流中讀取參數,如下代碼所示:

 

String body = "";
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
    inputStream = request.getInputStream();
    if (inputStream != null) {
        bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        char[] charBuffer = new char[128];
        int bytesRead = -1;
        while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
            stringBuilder.append(charBuffer, 0, bytesRead);
        }
    } else {
        stringBuilder.append("");
    }
} catch (IOException ex) {
    e.printStackTrace();
} finally {
    if (inputStream != null) {
        try {
            inputStream.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
    if (bufferedReader != null) {
        try {
            bufferedReader.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}
body = stringBuilder.toString();

代碼中的body就是request中的參數,我這裏傳的是JSON數據:{"page": 1, "pageSize": 10},那麼body就是:body = "{"page": 1, "pageSize": 10}",一個JSON字符串。這樣是可以成功獲取到post請求的body,但是,經過過濾器後,參數經過@RequestBody註解賦值給controller中的方法的時候,卻拋出了一個這樣的異常:

org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing

在網上查找資料後發現,request的輸入流只能讀取一次,那麼這是爲什麼呢?下面是答案:

那是因爲流對應的是數據,數據放在內存中,有的是部分放在內存中。read 一次標記一次當前位置(mark position),第二次read就從標記位置繼續讀(從內存中copy)數據。 所以這就是爲什麼讀了一次第二次是空了。 怎麼讓它不爲空呢?只要inputstream 中的pos 變成0就可以重寫讀取當前內存中的數據。javaAPI中有一個方法public void reset() 這個方法就是可以重置pos爲起始位置,但是不是所有的IO讀取流都可以調用該方法!ServletInputStream是不能調用reset方法,這就導致了只能調用一次getInputStream()。

摘自:https://blog.csdn.net/sdut406/article/details/81369983

那麼有什麼辦法可以用戶解決呢?上面這篇博客中提到了解決方案,就是重寫HttpServletRequestWrapper把request保存下來,然後通過過濾器把保存下來的request再填充進去,這樣就可以多次讀取request了。步驟如下所示:

①寫一個類,繼承HttpServletRequestWrapper

 

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

public class RequestWrapper extends HttpServletRequestWrapper {
    private final String body;

    public RequestWrapper(HttpServletRequest request) {
        super(request);
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {

        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        body = stringBuilder.toString();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            @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 byteArrayInputStream.read();
            }
        };
        return servletInputStream;

    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    public String getBody() {
        return this.body;
    }

}

 

②過濾器Filter,用來把request傳遞下去

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.OncePerRequestFilter;

import com.cetc.common.utils.CommonUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Date;
import java.util.Map;

/**
 * @program: springboot
 * @description:
 * @author: Sid
 * @date: 2018-11-19 09:21
 * @since: 1.0
 **/
@Order(0)
/**
 * 註冊過濾器
 * */
@WebFilter(filterName = "RequestResponseLogFilter", urlPatterns = "/*")
public class RequestFilter extends OncePerRequestFilter {
    private static final Logger logger = LoggerFactory.getLogger(RequestFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

        try {
            RequestWrapper requestWrapper = new RequestWrapper(request);
            String body = requestWrapper.getBody();
            String parameterStr =
                "接收到請求 時間:" + CommonUtils.DEFAULT_DTF.format(new Date()) + ". 地址:" + request.getRequestURI()
                + "request ContentType: " + request.getContentType() + ". 參數:" + body;
            
            logger.debug(parameterStr);
            filterChain.doFilter(requestWrapper, response);
        } catch (Exception e) {
            logger.error("權限判斷出錯", e);
        }
    }

}

③在啓動類中註冊過濾器

 

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@SpringBootApplication
@ServletComponentScan  //註冊過濾器註解
@Configuration
public class WebApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }
}

經測試,問題解決

 

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