springboot配置過濾器導致的controller入參丟失問題
阿新 • • 發佈:2022-01-28
springmvc中 配置過濾器導致的post請求引數丟失問題
問題描述:
專案新增加功能,需要新增介面呼叫的入參驗籤,新增新增攔截器,並且配置了自定義BodyReaderHttpServletRequestWrapper實現流的複用,在不同的springboot版本中產生以下問題:
介面傳送前提:POST multipart/form-data 請求
springboot 1.5.3.RELEASE 中發現過濾器中無法獲取parameters 而controller層能獲取到
springboot 2.2.5.RELEASE 中發現過濾器能獲取parameters 而controller層不能獲取到
//過濾器程式碼 @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletRequest requestWrapper = null; HttpServletRequest httpRequest = (HttpServletRequest) request; String contentType = httpRequest.getContentType(); if (request instanceof HttpServletRequest) { // 將請求物件包裝為 可重複讀取流的請求物件。注意:構造好了,但是需要在攔截器中獲取 /** 也可以使用 ContentCachingRequestWrapper **/ requestWrapper= new ContentCachingRequestWrapper((HttpServletRequest) request); //自定義的wapper // requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request); } chain.doFilter(requestWrapper, response); }else{ chain.doFilter(request, response); } return; }
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { private byte[] requestBody = null;// 用於將流儲存下來 public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); //注意此處已經調過inputStream requestBody = StreamUtils.copyToByteArray(request.getInputStream()); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody); return new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } }
根本原因 request的getInputStream()/getReader() 與 getParameter() 的衝突問題
//程式碼定位
//檢視request類的方法
@Override
public String getParameter(String name) {
if (!parametersParsed) {
//進入
parseParameters();
}
return coyoteRequest.getParameters().getParameter(name);
}
//問題在此處 如果已經呼叫過getInputStream/getReader 此次會直接返回
if (usingInputStream || usingReader) {
success = true;
return;
}
//此處如此設計是因為Servlet3.1有相關規範如下圖
springboot版本不同 引起的結果不同:
在類WebMvcAutoConfiguration中有所不同
新版本中
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
老版本中
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
老版本中生效 而新版本不生效
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter(requestToUse, response);
}
spring 2.2.5解決辦法:
/** 第一種 先呼叫獲取引數 解決getParameterNames 和getInputStream 衝突問題 **/
Map<String, String[]> parameterMap = request.getParameterMap();
log.info("請求引數:{}", JSON.toJSONString(parameterMap));
if (request instanceof HttpServletRequest) {
// 將請求物件包裝為 可重複讀取流的請求物件。注意:構造好了,但是需要在攔截器中獲取
requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
chain.doFilter(requestWrapper, response);
}else {
chain.doFilter(request, response);
}
return;
參考文件
Request重複讀取流 - 簡書 (jianshu.com)
記一次getParameter()獲取不到引數問題的排查 - litter-chick - 部落格園 (cnblogs.com)