• 过滤器第二篇【高级应用】


    前言

    在上篇博文中,我们已经讲解了过滤器的基本概念,使用以及简单的Servlet应用了。这篇博文主要讲解过滤器的高级应用。

    编码过滤器

    目的:解决全站的乱码问题

    开发过滤器

    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
    
            //将request和response强转成http协议的
            HttpServletRequest httpServletRequest = (HttpServletRequest) req;
            HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
    
            httpServletRequest.setCharacterEncoding("UTF-8");
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("text/html;charset=UTF-8");
    
            chain.doFilter(httpServletRequest, httpServletResponse);
        }
    
    

    第一次测试

    Servlet1中向浏览器回应中文数据,没有出现乱码。

    
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            response.getWriter().write("看完博客点赞!");
    
        }
    

    这里写图片描述


    分析

    上面的过滤器是不完善的,因为浏览器用get方式提交给服务器的中文数据,单单靠上面的过滤器是无法完成的!

    那么我们需要怎么做呢??我们之前解决get方式的乱码问题是这样的:使用request获取传递过来的数据,经过ISO 8859-1反编码获取得到不是乱码的数据(传到Servlet上的数据已经被ISO 8859-1编码过了,反编码就可以获取原来的数据),再用UTF-8编码,得到中文数据!

    参考我之前的博文:http://blog.csdn.net/hon_3y/article/details/54632004#t9

    在Servlet获取浏览器以GET方式提交过来的中文是乱码的根本原因是:getParameter()方法是以ISO 8859-1的编码来获取浏览器传递过来的数据的,得到的是乱码

    既然知道了根本原因,那也好办了:过滤器传递的request对象,使用getParameter()方法的时候,获取得到的是正常的中文数据

    也就是说,sun公司为我们提供的request对象是不够用的,因为sun公司提供的request对象使用getParameter()获取get方式提交过来的数据是乱码,于是我们要增强request对象(使得getParameter()获取得到的是中文)!

    增强request对象

    增强request对象,我们要使用包装设计模式!

    包装设计模式的五个步骤:

    • 1、实现与被增强对象相同的接口
    • 2、定义一个变量记住被增强对象
    • 3、定义一个构造器,接收被增强对象
    • 4、覆盖需要增强的方法
    • 5、对于不想增强的方法,直接调用被增强对象(目标对象)的方法

    sun公司也知道我们可能对request对象的方法不满意,于是提供了HttpServletRequestWrapper类给我们实现(如果实现HttpServletRequest接口的话,要实现太多的方法了!)

    
    
        class MyRequest extends HttpServletRequestWrapper {
    
            private HttpServletRequest request;
    
            public MyRequest(HttpServletRequest request) {
                super(request);
                this.request = request;
            }
    
            @Override
            public String getParameter(String name) {
                String value = this.request.getParameter(name);
    
                if (value == null) {
                    return null;
                }
    
                //如果不是get方法的,直接返回就行了
                if (!this.request.getMethod().equalsIgnoreCase("get")) {
                    return null;
                }
    
                try {
    
                    //进来了就说明是get方法,把乱码的数据
                    value = new String(value.getBytes("ISO8859-1"), this.request.getCharacterEncoding());
                    return value ;
    
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
    
                    throw new RuntimeException("不支持该编码");
                }
    
            }
        }
    
    

    将被增强的request对象传递给目标资源,那么目标资源使用request调用getParameter()方法的时候,获取得到的就是中文数据,而不是乱码了!

    
            //将request和response强转成http协议的
            HttpServletRequest httpServletRequest = (HttpServletRequest) req;
            HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
    
            httpServletRequest.setCharacterEncoding("UTF-8");
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("text/html;charset=UTF-8");
    
            MyRequest myRequest = new MyRequest(httpServletRequest);
    
            //传递给目标资源的request是被增强后的。
            chain.doFilter(myRequest, httpServletResponse);
    

    第二次测试

    • 使用get方式传递中文数据给服务器
    
    <form action="${pageContext.request.contextPath}/Servlet1" method="get">
    
        <input type="hidden" name="username" value="中国">
    
    
        <input type="submit" value="提交">
    </form>
    

    这里写图片描述


    敏感词的过滤器

    如果用户输入了敏感词(傻b、尼玛、操蛋等等不文明语言时),我们要将这些不文明用于屏蔽掉,替换成符号!

    要实现这样的功能也很简单,用户输入的敏感词肯定是在getParameter()获取的,我们在getParameter()得到这些数据的时候,判断有没有敏感词汇,如果有就替换掉就好了!简单来说:也是要增强request对象

    增强request对象

    
        class MyDirtyRequest extends HttpServletRequestWrapper {
    
            HttpServletRequest request;
    
            //定义一堆敏感词汇
            private List<String> list = Arrays.asList("傻b", "尼玛", "操蛋");
    
            public MyDirtyRequest(HttpServletRequest request) {
                super(request);
                this.request = request;
            }
    
            @Override
            public String getParameter(String name) {
    
                String value = this.request.getParameter(name);
    
                if (value == null) {
                    return null;
                }
    
                //遍历list集合,看看获取得到的数据有没有敏感词汇
                for (String s : list) {
    
                    if (s.equals(value)) {
                        value = "*****";
                    }
                }
    
                return value ;
            }
        }
    

    开发过滤器

    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
    
            //将request和response强转成http协议的
            HttpServletRequest httpServletRequest = (HttpServletRequest) req;
            HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
    
            MyDirtyRequest dirtyRequest = new MyDirtyRequest(httpServletRequest);
    
            //传送给目标资源的是被增强后的request对象
            chain.doFilter(dirtyRequest, httpServletResponse);
        }
    

    测试

    这里写图片描述


    压缩资源过滤器

    按照过滤器的执行顺序:执行完目标资源,过滤器后面的代码还会执行。所以,我们在过滤器中可以获取执行完目标资源后的response对象!

    我们知道sun公司提供的response对象调用write()方法,是直接把数据返回给浏览器的。我们要想实现压缩的功能,write()方法就不能直接把数据写到浏览器上!

    这和上面是类似的,过滤器传递给目标资源的response对象就需要被我们增强,使得目标资源调用writer()方法的时候不把数据直接写到浏览器上

    增强response对象

    response对象可能会使用PrintWriter或者ServletOutputStream对象来调用writer()方法的,所以我们增强response对象的时候,需要把getOutputSteam和getWriter()重写

    
        class MyResponse extends HttpServletResponseWrapper{
    
            HttpServletResponse response;
            public MyResponse(HttpServletResponse response) {
                super(response);
                this.response = response;
            }
    
    
            @Override
            public ServletOutputStream getOutputStream() throws IOException {
                return super.getOutputStream();
            }
    
            @Override
            public PrintWriter getWriter() throws IOException {
                return super.getWriter();
            }
        }
    
    

    接下来,ServletOutputSteam要调用writer()方法,使得它不会把数据写到浏览器上。这又要我们增强一遍了!

    增强ServletOutputSteam

    
        /*增强ServletOutputSteam,让writer方法不把数据直接返回给浏览器*/
        class MyServletOutputStream extends ServletOutputStream{
    
            private ByteArrayOutputStream byteArrayOutputStream;
    
            public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
                this.byteArrayOutputStream = byteArrayOutputStream;
            }
    
            //当调用write()方法的时候,其实是把数据写byteArrayOutputSteam上
            @Override
            public void write(int b) throws IOException {
                this.byteArrayOutputStream.write(b);
    
            }
        }
    
    

    增强PrintWriter

    PrintWriter对象就好办了,它本来就是一个包装类,看它的构造方法,我们直接可以把ByteArrayOutputSteam传递给PrintWriter上。

    这里写图片描述

    
        @Override
        public PrintWriter getWriter() throws IOException {
            printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));
    
            return printWriter;
        }
    

    获取缓存数据

    我们把数据都写在了ByteArrayOutputSteam上了,应该提供方法给外界过去缓存中的数据!

    
        public byte[] getBuffer() {
    
            try {
    
                //防止数据在缓存中,要刷新一下!
                if (printWriter != null) {
                    printWriter.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.flush();
                    return byteArrayOutputStream.toByteArray();
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    

    增强response的完整代码

    
    class MyResponse extends HttpServletResponseWrapper{
    
        private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    
        private PrintWriter printWriter ;
    
        private HttpServletResponse response;
        public MyResponse(HttpServletResponse response) {
            super(response);
            this.response = response;
        }
    
    
        @Override
        public ServletOutputStream getOutputStream() throws IOException {
    
            //这个的ServletOutputSteam对象调用write()方法的时候,把数据是写在byteArrayOutputSteam上的
            return new MyServletOutputStream(byteArrayOutputStream);
        }
    
        @Override
        public PrintWriter getWriter() throws IOException {
            printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));
    
            return printWriter;
        }
    
        public byte[] getBuffer() {
    
            try {
    
                //防止数据在缓存中,要刷新一下!
                if (printWriter != null) {
                    printWriter.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.flush();
                    return byteArrayOutputStream.toByteArray();
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    过滤器

    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
    
    
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) resp;
            MyResponse myResponse = new MyResponse(response);
    
            //把被增强的response对象传递进去,目标资源调用write()方法的时候就不会直接把数据写在浏览器上了
            chain.doFilter(request, myResponse);
    
            //得到目标资源想要返回给浏览器的数据
            byte[] bytes = myResponse.getBuffer();
    
            //输出原来的大小
            System.out.println("压缩前:"+bytes.length);
    
    
            //使用GZIP来压缩资源,再返回给浏览器
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
            gzipOutputStream.write(bytes);
    
            //得到压缩后的数据
            byte[] gzip = byteArrayOutputStream.toByteArray();
    
            System.out.println("压缩后:" + gzip.length);
    
            //还要设置头,告诉浏览器,这是压缩数据!
            response.setHeader("content-encoding", "gzip");
            response.setContentLength(gzip.length);
            response.getOutputStream().write(gzip);
    
        }
    

    测试

    • 在Servlet上输出一大段文字:
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            response.getWriter().write("fdshfidsuhfidusfhuidsfhuidshdsuifhsd" +
                    "uifhsduifffffdshfidsuhfidusfhuidsfhuidshdsuif" +
                    "hsduifhsduifffffdshfidsuhfidusfhuidsfhuidshd" +
                    "suifhsduifhsduifffffdshfidsuhfidusfhuidsfhuidsh" +
                    "dsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuids" +
                    "hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuid" +
                    "shdsuifhsduifhsduiffdshfidsuhfidusfhuidsfhuids" +
                    "hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhui" +
                    "dshdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfh" +
                    "uidshdsuifhsduifhsduifffffdshfidsuhfidusfhuids" +
                    "fhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhuid" +
                    "sfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhui" +
                    "dsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfh" +
                    "uidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusf" +
                    "huidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidus" +
                    "fhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhfid" +
                    "usfhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhf" +
                    "idusfhuidsfhuidshdsuifhsduifhsd" +
                    "uifffffdshfidsuhfidusfhuidsfhuidshdsuifhsduifhsduifffffff");
    
        }
    
    • 效果:

    这里写图片描述


    HTML转义过滤器

    只要把getParameter()获取得到的数据转义一遍,就可以完成功能了。

    增强request

    
    
    class MyHtmlRequest extends HttpServletRequestWrapper{
    
        private HttpServletRequest request;
    
        public MyHtmlRequest(HttpServletRequest request) {
            super(request);
            this.request = request;
        }
    
    
        @Override
        public String getParameter(String name) {
    
            String value = this.request.getParameter(name);
            return this.Filter(value);
    
        }
    
        public String Filter(String message) {
            if (message == null)
                return (null);
    
            char content[] = new char[message.length()];
            message.getChars(0, message.length(), content, 0);
            StringBuffer result = new StringBuffer(content.length + 50);
            for (int i = 0; i < content.length; i++) {
                switch (content[i]) {
                    case '<':
                        result.append("&lt;");
                        break;
                    case '>':
                        result.append("&gt;");
                        break;
                    case '&':
                        result.append("&amp;");
                        break;
                    case '"':
                        result.append("&quot;");
                        break;
                    default:
                        result.append(content[i]);
                }
            }
            return (result.toString());
    
        }
    
    }
    

    过滤器

    
    
    
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
    
    
    
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) resp;
            MyHtmlRequest myHtmlRequest = new MyHtmlRequest(request);
    
            //传入的是被增强的request!
            chain.doFilter(myHtmlRequest, response);
    
        }
    

    测试

    jsp代码:

    
        <form action="${pageContext.request.contextPath}/Servlet1" method="post">
    
    
            <input type="hidden" name="username" value="<h1>你好i好<h1>">
    
            <input type="submit" value="提交">
        </form>
    

    Servlet代码:

    
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            String value = request.getParameter("username");
            response.getWriter().write(value);
    
        }
    

    这里写图片描述


    缓存数据到内存中

    在前面我们已经做过了,让浏览器不缓存数据【验证码的图片是不应该缓存的】。

    现在我们要做的是:缓存数据到内存中【如果某个资源重复使用,不轻易变化,应该缓存到内存中】

    这个和压缩数据的Filter非常类似的,因为让数据不直接输出给浏览器,把数据用一个容器(ByteArrayOutputSteam)存起来。如果已经有缓存了,就取缓存的。没有缓存就执行目标资源!

    增强response对象

    class MyResponse extends HttpServletResponseWrapper {
    
        private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    
        private PrintWriter printWriter ;
    
        private HttpServletResponse response;
        public MyResponse(HttpServletResponse response) {
            super(response);
            this.response = response;
        }
    
    
        @Override
        public ServletOutputStream getOutputStream() throws IOException {
    
            //这个的ServletOutputSteam对象调用write()方法的时候,把数据是写在byteArrayOutputSteam上的
            return new MyServletOutputStream(byteArrayOutputStream);
        }
    
        @Override
        public PrintWriter getWriter() throws IOException {
            printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));
    
            return printWriter;
        }
    
        public byte[] getBuffer() {
    
            try {
    
                //防止数据在缓存中,要刷新一下!
                if (printWriter != null) {
                    printWriter.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.flush();
                    return byteArrayOutputStream.toByteArray();
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    
    //增强ServletOutputSteam,让writer方法不把数据直接返回给浏览器
    
    class MyServletOutputStream extends ServletOutputStream {
    
        private ByteArrayOutputStream byteArrayOutputStream;
    
        public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
            this.byteArrayOutputStream = byteArrayOutputStream;
        }
    
        //当调用write()方法的时候,其实是把数据写byteArrayOutputSteam上
        @Override
        public void write(int b) throws IOException {
            this.byteArrayOutputStream.write(b);
    
        }
    }
    

    过滤器

    
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
    
            //定义一个Map集合,key为页面的地址,value为内存的缓存
            Map<String, byte[]> map = new HashMap<>();
    
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) resp;
    
            //得到客户端想要请求的资源
            String uri = request.getRequestURI();
            byte[] bytes = map.get(uri);
    
            //如果有缓存,直接返回给浏览器就行了,就不用执行目标资源了
            if (bytes != null) {
                response.getOutputStream().write(bytes);
                return ;
            }
    
            //如果没有缓存,就让目标执行
            MyResponse myResponse = new MyResponse(response);
            chain.doFilter(request, myResponse);
    
            //得到目标资源想要发送给浏览器的数据
            byte[] b = myResponse.getBuffer();
    
            //把数据存到集合中
            map.put(uri, b);
    
            //把数据返回给浏览器
            response.getOutputStream().write(b);
    
    
        }
    

    测试

    尽管是刷新,获取得到的也是从缓存拿到的数据!

    这里写图片描述


    如果您觉得这篇文章帮助到了您,可以给作者一点鼓励



  • 相关阅读:
    oracle10G/11G官方迅雷下载地址合集(转载)
    Oracle数据库的登录以及常用数据导入查询
    Tomcat添加SSL网站证书配置
    SVN的安装与在IDEA中的配置
    01-Spring Boot配置文件详解
    微服务概况及注册中心搭建
    zk实现分布式锁
    ZooKeeper初识
    Reids集群知识
    redis初识
  • 原文地址:https://www.cnblogs.com/zhong-fucheng/p/7203033.html
Copyright © 2020-2023  润新知