ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

springboot配置过滤器导致的controller入参丢失问题

2022-01-28 12:03:30  阅读:281  来源: 互联网

标签:HttpServletRequest return springboot request 入参 controller Override new public


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)

标签:HttpServletRequest,return,springboot,request,入参,controller,Override,new,public
来源: https://www.cnblogs.com/mdsjuk/p/15852360.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有