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)

 

posted @ 2022-07-27 14:18  xiaoBai1001  阅读(630)  评论(1编辑  收藏  举报