全注解实现自定义过滤器与拦截请求参数
环境
springboot工程
步骤
1、在主Application上添加注解@ServletComponentScan使得@WebFilter注解生效
@SpringBootApplication
@ServletComponentScan
public class DemoApplication {
2、添加自定义过滤器
@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = null;
if (servletRequest instanceof HttpServletRequest) {
request = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) servletRequest);
}
JSONObject requestBody = new JSONObject();
//解析post请求体
if (RequestMethod.POST.name().equals(request.getMethod())) {
String body = HttpContentUtils.Reader.readPostBodyAsString(request);
if (StringUtils.isNotEmpty(body)) {
requestBody.putAll(JSONObject.parseObject(body));
}
}
//解析get请求体
Map<String, String[]> paramMap = request.getParameterMap();
if (paramMap != null && paramMap.size() > 0) {
for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
if (entry.getValue() != null && entry.getValue().length > 0) {
requestBody.put(entry.getKey(), entry.getValue()[0]);
}
}
}
//自定义过滤条件 默认通过
filterChain.doFilter(request, servletResponse);
}
@Override
public void destroy() {
}
}
3、因为要获取请求体内容,获取请求体的工具类如下:
public class HttpContentUtils {
public static class Reader {
public static String readPostBodyAsString(HttpServletRequest request)
{
BufferedReader br = null;
StringBuilder sb = new StringBuilder("");
try
{
br = request.getReader();
String str;
while ((str = br.readLine()) != null)
{
sb.append(str);
}
//br.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (null != br)
{
try
{
br.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
return sb.toString();
}
}
}
4、请求流只能读一次,无法重复读,所以需要先读出来,然后存储下来,留作以后重复读使用,通过继承HttpServletRequestWrapper类,重写getInputStream()方法,来实现数据重复读。
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] requestBody = null;//用于将流保存下来
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
requestBody = StreamUtils.copyToByteArray(request.getInputStream());
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@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()));
}
}
其他描述:
为什么不能通过拦截器实现解析请求体,然后进行拦截?
拦截器是spring基于aop反射实现的请求拦截,在这个过程中我们将request请求转换成我们的自定义的能够多次读取的请求类,无法将这个类传递下去。
过滤器是基于函数回调实现的请求拦截,我们在这个过程中替换掉请求体为我们自定义的请求体,能够做到对象替换,在这之后使用的getInputStream为我们重写的方法,能够实现重复读取流数据,流数据的生命周期和请求一致,所以使用过滤器来实现基于请求体参数过滤请求的功能。