项目中有些场景可能需要多次读取requestBody的内容,如日志打印,参数校验,而HttpServletRequest对象中的数据流只能读取一次,再次读取就会抛出异常,因此我们需要自定义request包装类实现多次读取请求体内容。
正常情况下,controller接口只能申明一次@RequestBody,写多个会报错。原因就是request请求体的流对象只能单次读取。
1.自定义包装类
public class RequestWrapper extends HttpServletRequestWrapper {
//参数字节数组
private byte[] requestBody;
//Http请求对象
private HttpServletRequest request;
public RequestWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public ServletInputStream getInputStream() throws IOException {
/**
* 每次调用此方法时将数据流中的数据读取出来,然后再回填到InputStream之中
* 解决通过@RequestBody和@RequestParam(POST方式)读取一次后控制器拿不到参数问题
*/
if (null == this.requestBody) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(request.getInputStream(), baos);
this.requestBody = baos.toByteArray();
}
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() {
return bais.read();
}
};
}
public byte[] getRequestBody() {
return requestBody;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
2. 新增过滤器对HttpServletRequest进行包装
@WebFilter(urlPatterns = "/*")
public class RequestWrapperFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
if(!(servletRequest instanceof HttpServletRequest)){
filterChain.doFilter(servletRequest, servletResponse);
return;
}
//将改为我们定义的包装类
ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
filterChain.doFilter(requestWrapper, servletResponse);
} catch (IOException | ServletException e) {
e.printStackTrace();
}
}
}
3. 测试
经过以上配置,接口参数就可以写多个RequestBody了
@PostMapping("/post")
public Object postTest(@RequestBody Book book,@RequestBody Book book2){
log.info("controller----->,{}",book);
return book;
}