• Filter和Interceptor 使用场景和原理(一)


    对技术进行不断总结和梳理来提升技能,作为10年小白程序员以后要在总结和梳理增多,以使在技术路上越来越好。看到此博客的小伙伴,如有疑问可以相互交流沟通。博客开始和结尾有相关代码实例下载链接

    代码实例:https://files.cnblogs.com/files/liyanbofly/filterIntecptorDemo.rar?t=1658933924

    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;
        /**
         * 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);
            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)

     

  • 相关阅读:
    Linux 工具箱—17款文件管理器
    RMAN 初学者指南
    Oracle监听器Server端与Client端配置实例
    mysqlproxy完成mysql读写分离
    linux端口映射
    MYSQL分布式集群使用主从复制
    SQL高级运用
    Redis安装与使用
    mysql cpu 负载率超过理想值,解决方案总结
    揭秘:HR是如何做背景调查的?你真的就可以隐瞒事实了吗?
  • 原文地址:https://www.cnblogs.com/liyanbofly/p/16524677.html
Copyright © 2020-2023  润新知