spring应用中多次读取http post方法中的流(附源码)

一、问题简述先说下为啥有这个需求,在基于spring的web应用中,一般会在controller层获取http方法body中的数据。方式1:比如http请求的content-type为application/json的情况下,直接用@RequestBody接收。方式2:也有像目前我们在做的这个项目,比较原始,是直接手动读取流。(不要问我为啥这么原始,第一版也不是我写的。)@RequestMappi...

spring应用中多次读取http post方法中的流(附源码)

一、问题简述

先说下为啥有这个需求,在基于spring的web应用中,一般会在controller层获取http方法body中的数据。

方式1:

比如http请求的content-type为application/json的情况下,直接用@RequestBody接收。

方式2:

也有像目前我们在做的这个项目,比较原始,是直接手动读取流。(不要问我为啥这么原始,第一版也不是我写的。)

@RequestMapping("/XXX.do") public void XXX(HttpServletRequest request, HttpServletResponse response) throws IOException {  JSONObject jsonObject = WebUtils.getParameters(request);     //业务处理
  ResponseUtil.setResponse(response, MessageFactory.createSuccessMsg()); }

WebUtils.getParameters如下:
 public static JSONObject getParameters(HttpServletRequest request) throws IOException {  InputStream is = null;  is = new BufferedInputStream(request.getInputStream(), BUFFER_SIZE);  int contentLength = Integer.valueOf(request.getHeader("Content-Length"));  byte[] bytes = new byte[contentLength];  int readCount = 0;  while (readCount < contentLength) {readCount  = is.read(bytes, readCount, contentLength - readCount);  }  String requestJson = new String(bytes, AppConstants.UTF8);  if (StringUtils.isBlank(requestJson)) {return new JSONObject();  }  JSONObject jsonObj = JsonUtils.toJSONObject(requestJson);  return jsonObj; }

当然,不管怎么说,都是对流进行读取。

问题是,假如我想在controller前面加一层aop,aop里面对进入controller层的方法进行日志记录,记录方法参数,应该怎么办呢。

如果是采用了方式1的话,简单。spring已经帮我们把参数从流里取出来,给我们提供好了,我们拿着打印一下日志即可。

如果是比较悲剧地采用了我们这种方式,参数里只有个httpServletRequest,那就只有自己去读取流了,然而,在aop中我们把流读了的话,

在controller层就读不到了。

毕竟,流只能读一次啊。

二、怎么一个流读多次呢

说一千道一万,流来自哪里,来自

javax.servlet.ServletRequest#getInputStream

所以,我们的思路,是不是可以这样,定义一个filter,在filter中将request替换为我们自定义的request。

下面标红的为自定义的request。

/** * */package com.ckl.filter;import com.ckl.utils.BaseWebUtils;import com.ckl.utils.MultiReadHttpServletRequest;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.core.annotation.Order;import org.springframework.http.HttpMethod;import org.springframework.http.MediaType;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import java.io.IOException;/** * Web流多次读写过滤器 * * 拦截所有请求,主要是针对第三方提交过来的请求. * 为什么要做成可多次读写的流,因为可以在aop层打印日志。 * 但是不影响controller层继续读取该流 * * 该filter的原理:https://stackoverflow.com/questions/10210645/http-servlet-request-lose-params-from-post-body-after-read-it-once/17129256#17129256 * @author ckl */@Order(1)@WebFilter(filterName = "cacheRequestFilter", urlPatterns = "*.do")public class CacheRequestFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(CacheRequestFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException {  // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  HttpServletRequest httpServletRequest = (HttpServletRequest) request;  logger.info("request uri:{}",httpServletRequest.getRequestURI());  if (BaseWebUtils.isFormPost(httpServletRequest)){httpServletRequest = new MultiReadHttpServletRequest(httpServletRequest);String parameters = BaseWebUtils.getParameters(httpServletRequest);logger.info("CacheRequestFilter receive post req. body is {}", parameters);  }else if (isPost(httpServletRequest)){//文件上传请求,没必要缓存请求if (request.getContentType().contains(MediaType.MULTIPART_FORM_DATA_VALUE)){}else { httpServletRequest = new MultiReadHttpServletRequest(httpServletRequest); String parameters = BaseWebUtils.getParameters(httpServletRequest); logger.info("CacheRequestFilter receive post req. body is {}", parameters);}  }  chain.doFilter(httpServletRequest, response); } @Override public void destroy() {  // TODO Auto-generated method stub } public static boolean isPost(HttpServletRequest request) {  return  HttpMethod.POST.matches(request.getMethod()); }}
MultiReadHttpServletRequest.java:
import org.apache.commons.io.IOUtils;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import java.io.BufferedReader;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStreamReader;/** * desc: * https://stackoverflow.com/questions/10210645/http-servlet-request-lose-params-from-post-body-after-read-it-once/17129256#17129256 * @author : ckl * creat_date: 2018/8/2 0002 * creat_time: 13:46 **/public class MultiReadHttpServletRequest extends HttpServletRequestWrapper { private ByteArrayOutputStream cachedBytes; public MultiReadHttpServletRequest(HttpServletRequest request) {  super(request);  cachedBytes = new ByteArrayOutputStream();  ServletInputStream inputStream = null;  try {inputStream = super.getInputStream();IOUtils.copy(inputStream, cachedBytes);  } catch (IOException e) {e.printStackTrace();  } } @Override public ServletInp
源文地址:https://www.guoxiongfei.cn/cntech/4168.html