热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

springmvc中配置过滤器导致的post请求参数丢失问题问题描述:​项目新增加功能,需要添加接口调用的入参验签,新增添加拦截器,并且配置了自定义BodyReaderHttpS

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 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)



推荐阅读
  • 使用 ModelAttribute 实现页面数据自动填充
    本文介绍了如何利用 Spring MVC 中的 ModelAttribute 注解,在页面跳转后自动填充表单数据。主要探讨了两种实现方法及其背后的原理。 ... [详细]
  • 本文详细介绍了Socket在Linux内核中的实现机制,包括基本的Socket结构、协议操作集以及不同协议下的具体实现。通过这些内容,读者可以更好地理解Socket的工作原理。 ... [详细]
  • 本文详细介绍了如何正确设置Shadowsocks公共代理,包括调整超时设置、检查系统限制、防止滥用及遵守DMCA法规等关键步骤。 ... [详细]
  • 本文将从基础概念入手,详细探讨SpringMVC框架中DispatcherServlet如何通过HandlerMapping进行请求分发,以及其背后的源码实现细节。 ... [详细]
  • 本文将详细介绍如何配置并整合MVP架构、Retrofit网络请求库、Dagger2依赖注入框架以及RxAndroid响应式编程库,构建高效、模块化的Android应用。 ... [详细]
  • Kubernetes Services详解
    本文深入探讨了Kubernetes中的服务(Services)概念,解释了如何通过Services实现Pods之间的稳定通信,以及如何管理没有选择器的服务。 ... [详细]
  • 本文详细介绍了在PHP中如何获取和处理HTTP头部信息,包括通过cURL获取请求头信息、使用header函数发送响应头以及获取客户端HTTP头部的方法。同时,还探讨了PHP中$_SERVER变量的使用,以获取客户端和服务器的相关信息。 ... [详细]
  • Spring Security基础配置详解
    本文详细介绍了Spring Security的基础配置方法,包括如何搭建Maven多模块工程以及具体的安全配置步骤,帮助开发者更好地理解和应用这一强大的安全框架。 ... [详细]
  • JavaScript 跨域解决方案详解
    本文详细介绍了JavaScript在不同域之间进行数据传输或通信的技术,包括使用JSONP、修改document.domain、利用window.name以及HTML5的postMessage方法等跨域解决方案。 ... [详细]
  • 探讨多种方法来确定Java对象的实际类型,包括使用instanceof关键字、getClass()方法等。 ... [详细]
  • HDU 2537 键盘输入处理
    题目描述了一个名叫Pirates的男孩想要开发一款键盘输入软件,遇到了大小写字母判断的问题。本文提供了该问题的解决方案及实现方法。 ... [详细]
  • 本文介绍了如何通过安装和配置php_uploadprogress扩展来实现文件上传时的进度条显示功能。通过一个简单的示例,详细解释了从安装扩展到编写具体代码的全过程。 ... [详细]
  • iOS如何实现手势
    这篇文章主要为大家展示了“iOS如何实现手势”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“iOS ... [详细]
  • H5技术实现经典游戏《贪吃蛇》
    本文将分享一个使用HTML5技术实现的经典小游戏——《贪吃蛇》。通过H5技术,我们将探讨如何构建这款游戏的两种主要玩法:积分闯关和无尽模式。 ... [详细]
  • Beetl是一款先进的Java模板引擎,以其丰富的功能、直观的语法、卓越的性能和易于维护的特点著称。它不仅适用于高响应需求的大型网站,也适合功能复杂的CMS管理系统,提供了一种全新的模板开发体验。 ... [详细]
author-avatar
诺vs诺197
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有