Filter和Interceptor 使用场景和原理(一)
对技术进行不断总结和梳理来提升技能,作为10年小白程序员以后要在总结和梳理增多,以使在技术路上越来越好。看到此博客的小伙伴,如有疑问可以相互交流沟通。博客开始和结尾有相关代码实例下载链接
代码实例:https://files.cnblogs.com/files/liyanbofly/filterIntecptorDemo.rar?t=1673943641
Filter
Filter 原理使用说明:
Filter也称之为过滤器,它是Servlet技术中最实用的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能
1、filter是作用在interceptor(拦截器)之前,filter主要是依赖serlvet容器
2、Filter 对用户请求进行预处理,接着将请求交给Servlet 进行处理并生成响应,最后Filter 再对服务器响应进行后处理
3、Filter功能:
调用目标资源之前,让一段代码执行。
是否调用目标资源(即是否让用户访问web资源)。
在HttpServletRequest 到达 Servlet 之前,拦截客户的 HttpServletRequest ,执行相关的业务逻辑,如根据需要检查 HttpServletRequest 、也可以修改HttpServletRequest 头和数据。
在HttpServletResponse 到达客户端之前,拦截HttpServletResponse,执行相关的业务逻辑,如根据需要检查 HttpServletResponse 、也可以修改HttpServletResponse头和数据
以上都是定义相关下面描述一下实战应用
ServeletRequet 对象数据信息只能读取一次,如果想在Filter或Interceptor 使用ServletRequest中的数据就需要将ServletRequest进行封装,将其数据对象放入新封装对象当中变成可多次获取请求数据使用,
在Filter可以记录MDC.set 记录日志链信息,使后续日志都有如TraceId; 打印请求输入和响应输出参数
PS:
默认的HttpServletRequest和HttpServletResponse中的流被读取一次之后,再次读取会失败,所以要使用RequestWrapper和ResponseWrapper进行包装,实现重复读取。
实例代码
1)MyFilter
/** *1、filter是作用在interceptor(拦截器)之前,filter主要是依赖serlvet容器 *2、Filter 对用户请求进行预处理,接着将请求交给Servlet 进行处理并生成响应,最后Filter 再对服务器响应进行后处理 * */ @Component @WebFilter(value = "MyFilter",urlPatterns = "/*") public class MyFilter implements Filter { Logger logger= LoggerFactory.getLogger(MyFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("MyFilter-init"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("doFilter-01"); RequestWrapper requestWrapper=null; ResponseWrapper responseWrapper=null; // 如果是 HttpServletRequest 转换成可多次读取数据对象 if(request instanceof HttpServletRequest) { requestWrapper = new RequestWrapper((HttpServletRequest) request); } // 正常请求都会进 requestWrapper 都不会为null if(requestWrapper!=null){ // 可以判断内容类型 && "application/json".equals(((HttpServletRequest)request).getHeader("Content-Type") // 场景使用 记录MDC信息 setLogInfo(requestWrapper); // 增加ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response); // 可以使用requestWrapper 读取信息处理一些逻辑,也可以下放到Inteceptor 做逻辑处理 chain.doFilter(requestWrapper, responseWrapper); // 响应数据信息 if (responseWrapper != null) { String resStr = new String(responseWrapper.toByteArray(), response.getCharacterEncoding()); System.out.println(resStr); } }else{ // Map<String, String[]> mapPara= request.getParameterMap(); // String parameterJsonStr = JSON.toJSONString(request.getParameterMap()); chain.doFilter(request, response); } System.out.println("doFilter-02"); } @Override public void destroy() { System.out.println("MyFilter-destroy"); } private void setLogInfo(ServletRequest requestWrapper) { try { if (requestWrapper instanceof RequestWrapper) { RequestWrapper tempRequestWrapper = (RequestWrapper) requestWrapper; String body = tempRequestWrapper.getRequestBody(); JSONObject jsonObj = JSON.parseObject(body); String traceId = jsonObj.getOrDefault("traceId", StringUtils.EMPTY).toString(); MDC.put("traceId", traceId); // 也可以这样取 // Map<String, String[]> mapPara=requestWrapper.getParameterMap(); // String parameterJsonStr = JSON.toJSONString(request.getParameterMap()); // MDC.put("traceID", JSON.parseObject(parameterJsonStr).getString("traceID")); } } catch (Exception e) { logger.error("MDC设置失败,不影响接口请求。"); } } }
2)RequestWrapper 继承 HttpServletRequestWrapper 封装提供对象可使请求参数多次读取
/** * 对ServletRequest 的扩展使ServletReqeust 的请求数据可以多次读 * 该类使用 requestBody 记录 对ServletRequest 的数据流使其达到可以多次读取 */ public class RequestWrapper extends HttpServletRequestWrapper { private final byte[] requestBody;
// 加上该部分获取一下【】, 才能支持 content-Type是 application/x-www-form-urlencoded 请求数据 private Map<String, String[]> parameterMapData;
/** * Constructs a request object wrapping the given request. * * @param request The request to wrap * @throws IllegalArgumentException if the request is null */ public RequestWrapper(HttpServletRequest request) throws IOException { super(request);
// 加上该部分获取一下【】, 才能支持 content-Type是 application/x-www-form-urlencoded 请求数据 // 其实仅调用一一【request.getParameterMap()】该方法就可以将,请求参数放入request.getParam 相关对象, // 后续就可以获取 parameterMapData=request.getParameterMap()
requestBody = StreamUtils.copyToByteArray(request.getInputStream());
} @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody); return new ServletInputStream() { public boolean isFinished() { return false; } public boolean isReady() { return false; } public void setReadListener(ReadListener readListener) {} public int read() { return byteArrayInputStream.read(); } }; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(this.getInputStream())); } /** * 对外提供 记录servletRequest的数据 * @return */ public String getRequestBody() { return new String(this.requestBody); } }
3)ResponseWrapper 继承 HttpServletResponseWrapper 封装提供对象可使输出参数多次读取
public class ResponseWrapper extends HttpServletResponseWrapper { private final ByteArrayOutputStream bos = new ByteArrayOutputStream(); private PrintWriter writer = new PrintWriter(bos); /** * Constructs a response adaptor wrapping the given response. * * @param response The response to be wrapped * @throws IllegalArgumentException if the response is null */ public ResponseWrapper(HttpServletResponse response) { super(response); } @Override public ServletResponse getResponse() { return this; } @Override public ServletOutputStream getOutputStream() throws IOException { return new ServletOutputStream() { @Override public boolean isReady() { return false; } @Override public void setWriteListener(WriteListener writeListener) { } private TeeOutputStream tee = new TeeOutputStream(ResponseWrapper.super.getOutputStream(), bos); @Override public void write(int b) throws IOException { tee.write(b); } }; } @Override public PrintWriter getWriter() throws IOException { return new TeePrintWriter(super.getWriter(), writer); } public byte[] toByteArray(){ return bos.toByteArray(); } }
4)TeePrintWriter 为ResponseWrapper提供支持
/** * 为ResponseWrapper 提供对象支持 */ public class TeePrintWriter extends PrintWriter { PrintWriter branch; public TeePrintWriter(PrintWriter main, PrintWriter branch) { super(main, true); this.branch = branch; } public void write(char buf[], int off, int len) { super.write(buf, off, len); super.flush(); branch.write(buf, off, len); branch.flush(); } public void write(String s, int off, int len) { super.write(s, off, len); super.flush(); branch.write(s, off, len); branch.flush(); } public void write(int c) { super.write(c); super.flush(); branch.write(c); branch.flush(); } public void flush() { super.flush(); branch.flush(); } }
下接:Filter和Interceptor 使用场景和原理(二) - liyanbo - 博客园 (cnblogs.com)