昨天下午在开发的时候遇到了奇怪的事情,在SpringBoot
的Controller
里面直接使用HttpServletRequest
的getInputStream()
方法的时候获得的输入流无数据,通过getContentLength()
获得内容长度的时候又是有值的,由于昨天比较晚了就没有研究,今天花了点时间查一下原因。
出现这种情况,首先怀疑输入流已经被使用了,由于请求输入流是不带缓存的,使用一次后流就无效了,通常触发解析输入流就是调用了getParameter()
等方法,经过检查代码确认没有做过相关处理,所以怀疑SpringBoot
底层做了处理。
查了一下SpringBoot
的自动装配配置,在WebMvcAutoConfiguration
中初始化了一个OrderedHiddenHttpMethodFilter
,默认这个过滤器是生效的,相关代码如下:
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = true)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
OrderedHiddenHttpMethodFilter
继承了OrderedHiddenHttpMethodFilter
,而OrderedHiddenHttpMethodFilter
又继承了HiddenHttpMethodFilter
,在该类的doFilterInternal()
方法中发现有对参数做处理,相关代码如下:
@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);
}
至此就可以定位问题的所在了,找到了问题下面就来看看如何解决。
方案一:禁用默认的过滤器
SpringBoot
在自动装配的时候注入了OrderedHiddenHttpMethodFilter
,如果我们不需要该功能,在配置文件中显示的将其设置为false
。配置如下:
spring.mvc.hiddenmethod.filter.enabled=false
方案二:使用@RequestBody
注解
在需要获取请求输入流的方法上添加字节数组的参数,并添加@RequestBody
注解,示例代码如下:
@RequestMapping("/**")
public 返回值 方法名(@RequestBody byte[] body) {
// ...
}
方案三:自定义HiddenHttpMethodFilter
过滤器
参考代码如下:
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter(){
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
filterChain.doFilter(request, response);
}
};
}