1. 拦截器和过滤器
先说一下,过滤器和拦截器的区别和接洽。
1.1 雷同点
起首过滤器和拦截器都能实现哀求的筛选(过滤大概拦截),然后根据本身的业务需求,添加本身的逻辑,包管哀求以后走的时间数据能满意本身的需求。同时呢,他们又都能克制哀求流(过滤器只要不在过滤链中以后传request就形;拦截器返回false)。
1.2 差异点
1.2.1 实现原理差异
过滤器和拦截器 底层实现方式大不雷同,过滤器 是基于函数回调的,拦截器 则是基于Java的反射机制(动态署理)实现的
1.2.2 使用范围差异
过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中界说的,也就是说过滤器Filter 的使用要依靠于Servlet的,生存与Tomcat等服务器容器中,导致它只能在web步调中使用
拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依靠Tomcat等容器,是可以单独使用的。不但能应用在web步调中,也可以用于Application、Swing等步调中。
1.2.3 触发时机差异
由于过滤器和拦截器基于差异的容器,以是他们的触发时机和哀求中颠末容器的序次有关,Filter作用于Servlet,Interceptor作用于Springmvc。
1.2.4 处置处罚范围差异
过滤器处置处罚逻辑都在doFilter方法中,拦截器区分pre、post和after,分别粒度更细了,使用起来更机动
1.2.5 拦截器可以注入业务
拦截器可以获取IOC容器中的各个bean,而过滤器就不可,这点很紧张,在拦截器里注入一个service,可以调用业务逻辑。
2. Springboot添加拦截器
项目当中使用过滤器还是拦截器,根据需求来定,一般用哪种都可以,我是要处置处罚requestbody 中的数据(处置处罚特别字符,加密等)。由于数据还要往下继续传,以是选用filter。
过滤器添加有2种方法:
2.1 通过@WebFilter注解添加
- 添加@WebFilter注解,属性filterName设置过滤器名称,urlPatterns匹配要过滤的url
import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.annotation.WebFilter;@WebFilter(filterName = "testFilter", urlPatterns = "/*")public class TestFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("==过滤逻辑=="); chain.doFilter(request, response); }}
- 在启动类上添加@ServletComponentScan注解,添加这个注解后,@WebServlet、@WebFilter、@WebListener注解都可以被扫描到了。
2.2 使用FilterRegistrationBean对象,将过滤器添加到spring容器,需共同@Configuration和@Bean注解
- 写过滤器类,实现Filter接口,并添加 @Component注解
import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import org.apache.http.entity.ContentType;import org.springframework.http.HttpMethod;import org.springframework.stereotype.Component;/** * gateway转发参数时,有特别符号如 * +等颠末了urlencode,无法精确吸收, * 加过滤器将 body进行urldecode * * 留意的标题:RequestBody注解的数据, * 要通过request的getReader和getInputStream()方法来获取流数据,然后变化为字符串的 * 但是流读取一次后就关闭了,在filter中读取了以后,controller中就拿不到数据了 * 为此,我们自界说一个HttpServletRequestWrapper对象,将拿到的数据在放到这个对象中, * 并将这个对象从chain.doFilter()方法中继续传下去,后续就能继续读取了 */@Componentpublic class RequestBodyFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String contextPath = request.getServletContext().getContextPath(); boolean b1 = HttpMethod.POST.name().equals(req.getMethod()); boolean b2 = ContentType.APPLICATION_JSON.getMimeType().equals(request.getContentType()); boolean b3 = request.getParameterMap().isEmpty(); boolean b4 = contextPath.contains("/test"); if (b1 && b2 && b3 && b4) { MyHttpServletRequestWrapper requestWrapper = new MyHttpServletRequestWrapper(req); chain.doFilter(requestWrapper, response); } else { chain.doFilter(request, response); } }}
- 将过滤器添加到FilterRegistrationBean,并交给spring容器
import javax.servlet.Filter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class MyFilterConfig { @Autowired private RequestBodyFilter requestBodyFilter; /** * 将过滤器注册到FilterRegistrationBean中 * 进行简单配置 */ @Bean public FilterRegistrationBean<Filter> someFilterRegistration() { FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>(); registration.setFilter(requestBodyFilter); registration.addUrlPatterns("/*"); registration.setName("requestBodyFilter"); registration.setOrder(99); return registration; }}
- 踩的坑,由于在过滤器中通过request的getReader和getInputStream()方法来获取了流数据,但是流读取一次后就关闭了,在filter中读取了以后,controller中就拿不到数据了。为此,我们自界说一个HttpServletRequestWrapper对象,将拿到的数据在放到这个对象中, 并将这个对象从chain.doFilter()方法中继续传下去,后续就能继续读取了。
import java.io.BufferedReader;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;import java.net.URLDecoder;import java.nio.CharBuffer;import java.nio.charset.Charset;import javax.servlet.ReadListener;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;/** * 自界说request对象,将body处置处罚后,放到这个对象内里, * 一定要重写getReader()和getInputStream()方法,将body放进去, * 如许反面的controller才气从这个request的getReader()和getInputStream() * 方法中拿到新的body对象 */public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper { private byte[] body; public MyHttpServletRequestWrapper(HttpServletRequest request) { super(request); final StringBuilder builder = new StringBuilder(); // 缓存按 8192 巨细 final CharBuffer buffer = CharBuffer.allocate(2 << 12); BufferedReader reader = null; try { reader = request.getReader(); while (-1 != reader.read(buffer)) { builder.append(buffer.flip()); } } catch (Exception e) { e.printStackTrace(); } finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } String bodyStr = builder.toString(); try { bodyStr = URLDecoder.decode(bodyStr, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); throw new RuntimeException("body解码堕落!"); } body = bodyStr.getBytes(Charset.defaultCharset()); } public byte[] getBody() { return body; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public boolean isFinished() { return bais.available() == 0; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() throws IOException { return bais.read(); } }; }}总结
- 相识过滤器和拦截器的区别
- 过滤器保举使用第2种方法,使用spring管理,可以设置order
- 过滤器中假如读取了body数据,必要回写归去,让后续使用能读取到body
|