SpringBoot通过请求对象获取输入流无数据

网友投稿 390 2022-08-19


SpringBoot通过请求对象获取输入流无数据

目录请求对象获取输入流无数据问题方案一:禁用默认的过滤器方案二:使用@RequestBody注解方案三:自定义HiddenHttpMethodFilter过滤器request输入流重复可读自定义类继承 HttpServletRequestWrapper定义一个过滤器 Filter创建过滤器配置类 FilterConfig

请求对象获取输入流无数据问题

昨天下午在开发的时候遇到了奇怪的事情,在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);

}

};

request输入流重复可读

自定义类继承 HttpServletRequestWrapper

/**

* @describe 目的是让其输入流可重复读

* @author czx

* @date 2020年5月15日22http://:53:35

*/

@Slf4j

public class RequestWrapper extends HttpServletRequestWrapper {

/**

* 存储body数据的容器

*/

private final byte[] body;

public RequestWrapper(HttpServletRequest request) throws IOException {

super(request);

// 将body数据存储起来

String bodyStr = getBodyString(request);

body = bodyStr.getBytes(Charset.defaultCharset());

}

/**

* 获取请求Body

*

* @param request request

* @return String

*/

public String getBodyString(final ServletRequest request) {

try {

return inputStream2String(request.getInputStream());

} catch (IOException e) {

log.error("", e);

throw new RuntimeException(e);

}

}

/**

* 获取请求Body

*

* @return String

*/

public String getBodyString() {

final InputStream inputStream = new ByteArrayInputStream(body);

return inputStream2String(inputStream);

}

/**

* 将inputStream里的数据读取出来并转换成字符串

*

* @param inputStream inputStream

* @return String

*/

private String inputStream2String(InputStream inputStream) {

StringBuilder sb = new StringBuilder();

BufferedReader reader = null;

try {

reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));

String line;

while ((line = reader.readLine()) != null) {

sb.append(line);

}

} catch (IOException e) {

log.error("", e);

throw new RuntimeException(e);

} finally {

if (reader != null) {

try {

reader.close();

} catch (IOException e) {

log.error("", e);

}

}

}

return sb.toString();

}

@Override

public BufferedReader getReader() throws IOException {

return new BufferedReader(new InputStreamReader(getInputStream()));

}

@Override

public ServletInputStream getInputStream() throws IOException {

final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);

return new ServletInputStream() {

@Override

public int read() throws IOException {

return inputStream.read();

}

@Override

public boolean isFinished() {

return false;

}

@Override

public boolean isReady() {

return false;

}

@Override

public void setReadListener(ReadListener readListener) {

}

};

}

}

定义一个过滤器 Filter

@Slf4j

public class ReplaceStreamFilter implements Filter {

@Override

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request);

chain.doFilter(requestWrapper, response);

}

}

创建过滤器配置类 FilterConfig

@Configuration

public class FilterConfig {

/**

* 注册过滤器

*

* @return FilterRegistrationBean

*/

@Bean

public FilterRegistrationBean someFilterRegistration() {

FilterRegistrationBean registration = new FilterRegistrationBean();

registration.setFilter(replaceStreamFilter());

registration.addUrlPatterns("/*");

registration.setName("streamFilter");

return registration;

}

/**

* 实例化StreamFilter

* @return Filter

*/

@Bean(name = "replaceStreamFilter")

public Filter replaceStreamFilter() {

return new ReplaceStreamFilter();

}

}

完成以上步骤即可在拦截器中读取request中的body数据

@Slf4j

@Component

public class APIInterceptor implements HandlerInterceptor {

/**

* 预处理回调方法,实现处理器的预处理

* 返回值:true表示继续流程;false表示流程中断,不会继续调用其他的拦截器或处理器

*/

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{

log.info("开始拦截请求");

if(isjson(request)){

String jsonParam = new RequestWrapper(request).getBodyString();

JSONObject params = JSONObject.parseObject(jsonParam);

......

return true;

}

return false;

}

/**

* 返回json数据给前端

* @param response

* @param json

*/

protected void returnJson(ServletResponse response, JSONObject json){

PrintWriter writer = null;

response.setCharacterEncoding("UTF-8");

response.setContentType("application/json; charset=utf-8");

try {

writer = response.getWriter();

writer.print(json.toJSONString());

} catch (IOException e) {

log.error("response error",e);

} finally {

if (writer != nhttp://ull)

writer.close();

}

}

/**

* 判断本次请求的数据类型是否为json

* @param request request

* @return boolean

*/

private boolean isJson(HttpServletRequest request) {

if (request.getContentType() != null) {

return request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) ||

request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE);

}

return false;

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:springboot实现返回文件流
下一篇:springboot如何获取文件流
相关文章

 发表评论

暂时没有评论,来抢沙发吧~