需求
新项目和旧项目要做一个兼容,前端请求都按照新项目的参数格式(post请求)去访问我们的功能,旧项目所需要的参数格式是不一样的,就需要我们对参数的格式做一些处理
存在的问题
普通的参数可以从request的getParameterMap中获取,而@RequestBody的参数需要从request的InputStream中获取,但是InputStream只能读取一次,如果过滤器读取了参数,后面拦截器和controler层就读取不到参数了
为什么只能读取一次?
inputStream 中 read()是从输入流中读取下一个字节、如果以达到末尾侧返回-1,read 方法内pos 标识当前流每次流读取的位置、 每读取一次pos 做一次位移、直至结束返回-1:
如果想在此读取流中的值只需要把 pos 的值 rest 到初始位置就可以, rest方法是将 pos 值从新初始化为0,但是 request.getInputStream 方法返回的是 ServletInputStream 对象,ServletInputStream 并没有重写 rest() 方法,因在读取一次后 pos 值已经达到文件末尾、而 ServletInputStream 没有重写 rest() 方法、从而导致request.getInputStream 只能读取一次。
解决方案
- 新建自定义装饰类,继承HttpServletRequestWrapper,HttpServletRequestWrapper类是HttpServletRequest类的装饰类,HttpServletRequest继承ServletRequest类。(自定义装饰类的功能是对request进行处理,获得的是我们处理之后的request,和重写getInputStream方法)
- 使用filter,它可以在request到达servlet之前拦截ServletRequest对象
- 将拦截到的request放到自定义装饰类中
- 在自定义装饰类中对Request请求参数进行修改,存到装饰类的属性中
- 将装饰类放到过滤器链中(装饰类可以替代ServletRequest类—里氏转换, 重写getInputStream方法,那么调用的时候就是调用装饰类重写的getInputStream方法),如果没有下一个过滤器,就会到controller层,到具体方法之前,会进行参数解析,就是调用装饰类的getInputStream方法,此时获得的就是我们处理之后的request
代码
自定义装饰类
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] requestBody = null;//用于将流保存下来
private ApiRequest apiRequest;
public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
requestBody = IOUtils.toByteArray(request.getInputStream());
// 这里做request处理逻辑
}
}
@Override
public ServletInputStream getInputStream() {
// 返回的是我们处理之后的数据
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public int read() {
return bais.read(); // 读取 requestBody 中的数据
}
@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()));
}
public ApiRequest getApiRequest() {
return apiRequest;
}
public void setApiRequest(ApiRequest apiRequest) {
this.apiRequest = apiRequest;
}
}
过滤器
@Component
public class HttpServletRequestReplacedFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
MyHttpServletRequestWrapper requestWrapper = null;
if (request instanceof HttpServletRequest) {
requestWrapper = new MyHttpServletRequestWrapper((HttpServletRequest) request);
}
chain.doFilter(requestWrapper, response);
}
}
}