springboot配置過濾器導致的controller入參丟失問題


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)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM