复写HttpServlet多次获取请求body
场景需求
- 接口地址、接口请求全局过滤打印日志
- 接口报错时,通过钉钉等开放API发送警告信息
遇到问题
请求数据经过接口处理后,数据流已经关闭,无法再在报错处理中通过读取流的方式获取body数据,HttpServlet本身也没有类似getBody()的方法,所以无法在提醒中正确传递出问题接口的具体参数
解决思路
在全局过滤器中,对所有请求通过重载HttpServletRequestWrapper的getInputStream复写HttpServletRequest请求,将body参数读取出来后,存入请求的attribute属性中,后面如果有需要再次获取参数时,不再通过流读取,而是直接通过setAttribute方法获取。此外,body参数在HttpServletRequestWrapper中读取后,记得重置流读取标志位,不要影响后面接口的正常使用
代码实现
// RequestWrapper.java
import org.apache.commons.io.IOUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* @Description: 对HttpServletRequest进行重写,
* 1、用来接收application/json参数数据类型,即@RequestBody注解标注的参数,解决多次读取问题
* 2、用来解决注解@RequestParam通过POST/PUT/DELETE/PATCH方法传递参数,解决多次读取问题
* 首先看一下springboot控制器三个注解:
* 1、@PathVariable注解是REST风格url获取参数的方式,只能用在GET请求类型,通过getParameter获取参数
* 2、@RequestParam注解支持GET和POST/PUT/DELETE/PATCH方式,Get方式通过getParameter获取参数和post方式通过getInputStream或getReader获取参数
* 3、@RequestBody注解支持POST/PUT/DELETE/PATCH,可以通过getInputStream和getReader获取参数
*/
public class RequestWrapper extends HttpServletRequestWrapper {
/**
* 参数字节数组
*/
private byte[] requestBody;
/**
* Http请求对象
*/
private HttpServletRequest request;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
this.request = request;
}
/**
* @return
* @throws IOException
*/
@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();
// 后面需要拿请求体的地方通过getAttribute("body")获取
this.request.setAttribute("body", baos);
}
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();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
// ChannelFilter.java
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 全局接口请求拦截
*/
@Component
@WebFilter(filterName = "channelFilter", urlPatterns = {"/*"})
public class ChannelFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
try {
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest) {
requestWrapper = new RequestWrapper((HttpServletRequest) request);
}
if (requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}
@Override
public void destroy() {
}
}

浙公网安备 33010602011771号