preface

In the last blog post, we covered the basic concepts of filters, their use, and simple Servlet applications. This blog post focuses on advanced applications of filters.

Coding filter

Objective: To solve the problem of garbled code in the whole station

Development filter


    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        // Convert request and Response to 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);
    }


Copy the code

First test

Servlet1 returns Chinese data to the browser without garbled characters.


    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.getWriter().write("Like the blog after reading it!");

    }

Copy the code


Analysis of the

The filter above is not perfect, because the browser uses get to submit Chinese data to the server, relying on the filter above is not complete!

So what do we need to do? We solved the garble problem with get like this: Use REQUEST to obtain the data passed from, through ISO 8859-1 reverse encoding to obtain non-garbled data (the data transmitted to the Servlet has been ISO 8859-1 encoding, reverse encoding can obtain the original data), and then use UTF-8 encoding to obtain Chinese data!

Refer to my previous post: mp.weixin.qq.com/s?__biz=MzI…

The root cause of the garbled characters in the Servlet is that the getParameter() method uses ISO 8859-1 encoding to GET the data from the browser, resulting in garbled characters

Now that you know the root cause, that’s fine: The request object passed by the filter, when used with the getParameter() method, gets normal Chinese data

In other words, the Request object provided by Sun company is not enough, because the request object provided by Sun Company uses getParameter() to obtain the data submitted by get method is garbled. So we need to enhance the Request object (so that getParameter() gets Chinese)!

Enhanced Request object

To enhance the Request object, we’re going to use the wrapper design pattern!

The five steps of the packaging design pattern:

  • **1. Implement the same interface as the enhanced object **
  • 2. Define a variable to remember the enhanced object
  • Define a constructor that receives the enhanced object
  • 4. Cover methods that need to be enhanced
  • 5. For methods that do not want to be enhanced, call the methods of the enhanced object (the target object) directly

Sun also know that we may not satisfied with the way the request object, then provides the HttpServletRequestWrapper class for us to achieve (if realize it interface, in order to achieve too much way!)



	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;
	        }
	
	        // If it is not a get method, just return it
	        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("This code is not supported"); }}}Copy the code

If you pass the enhanced Request object to the target resource, the target resource will get Chinese data instead of garbled characters when it calls the request getParameter() method.


        // Convert request and Response to 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);

		// The request passed to the target resource is enhanced.
        chain.doFilter(myRequest, httpServletResponse);

Copy the code

Second test

  • Use get to pass Chinese data to the server

<form action="${pageContext.request.contextPath}/Servlet1" method="get">

    <input type="hidden" name="username" value="China">
    

    <input type="submit" value="Submit">
</form>

Copy the code


Filter for sensitive words

If users input sensitive words (stupid b, f * * k, f * * k, etc. Uncivilized language), we should shield these uncivilized words and replace them with symbols!

GetParameter () = getParameter(); getParameter() = getParameter(); getParameter() = getParameter(); In a nutshell: Also enhance the Request object

Enhanced Request object


	class MyDirtyRequest extends HttpServletRequestWrapper {
	
	    HttpServletRequest request;
	
	    // Define a bunch of sensitive words
	    private List<String> list = Arrays.asList("Silly b"."Nima"."Fucking");
	
	    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;
	        }
	
	        // Iterate through the list to see if there are sensitive terms in the obtained data
	        for (String s : list) {
	
	            if (s.equals(value)) {
	                value = "* * * * *"; }}returnvalue ; }}Copy the code

Development filter


    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        // Convert request and Response to HTTP
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        HttpServletResponse httpServletResponse = (HttpServletResponse) resp;

        MyDirtyRequest dirtyRequest = new MyDirtyRequest(httpServletRequest);

        // The enhanced Request object is passed to the target resource
        chain.doFilter(dirtyRequest, httpServletResponse);
    }

Copy the code

test


Compressed resource filter

Filters are executed in order: after the target resource is executed, the code following the filter is executed. So, we can get the response object after executing the target resource in the filter!

We know that the Response object provided by Sun calls the write() method and returns the data directly to the browser. For compression to work, the write() method cannot write data directly to the browser!

The response object that the filter passes to the target resource needs to be enhanced so that the target resource does not write the data directly to the browser when calling writer().

Enhancing the Response object

The response object may use PrintWriter or ServletOutputStream to call writer(), so when we enhance the response object, we need to rewrite getOutputSteam and getWriter()

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

Next, the ServletOutputSteam calls the writer() method so that it does not write the data to the browser. This is going to have to be enhanced again!

Enhance ServletOutputSteam


	/* Enhance ServletOutputSteam so that the Writer method does not return data directly to the browser */
	class MyServletOutputStream extends ServletOutputStream{
	
	    private ByteArrayOutputStream byteArrayOutputStream;
	
	    public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
	        this.byteArrayOutputStream = byteArrayOutputStream;
	    }
	
	    // When the write() method is called, data is actually written to byteArrayOutputSteam
	    @Override
	    public void write(int b) throws IOException {
	        this.byteArrayOutputStream.write(b); }}Copy the code

Strengthen the PrintWriter

PrintWriter object is easy, it’s a wrapper class, and by looking at its constructor, we can pass ByteArrayOutputSteam directly to PrintWriter.


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

        return printWriter;
    }

Copy the code

Fetching cached data

We wrote all the data in the ByteArrayOutputSteam, so we should provide methods for the data in the past cache!


    public byte[] getBuffer() {

        try {

            // To prevent data from being cached, refresh!
            if(printWriter ! =null) {
                printWriter.close();
            }
            if(byteArrayOutputStream ! =null) {
                byteArrayOutputStream.flush();
                returnbyteArrayOutputStream.toByteArray(); }}catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

Copy the code

Enhance the complete code for 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(a) throws IOException {

        // This ServletOutputSteam object writes data to byteArrayOutputSteam when it calls write()
        return new MyServletOutputStream(byteArrayOutputStream);
    }

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

        return printWriter;
    }

    public byte[] getBuffer() {

        try {

            // To prevent data from being cached, refresh!
            if(printWriter ! =null) {
                printWriter.close();
            }
            if(byteArrayOutputStream ! =null) {
                byteArrayOutputStream.flush();
                returnbyteArrayOutputStream.toByteArray(); }}catch (IOException e) {
            e.printStackTrace();
        }
        return null; }}Copy the code

The filter


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

        // Pass in the enhanced Response object so that the target resource does not write the data directly to the browser when calling write()
        chain.doFilter(request, myResponse);

        // Get the data that the target resource wants to return to the browser
        byte[] bytes = myResponse.getBuffer();

        // Output the original size
        System.out.println("Before compression:"+bytes.length);

        
        // Use GZIP to compress the resource and return it to the browser
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
        gzipOutputStream.write(bytes);
        
        // Get the compressed data
        byte[] gzip = byteArrayOutputStream.toByteArray();
        
        System.out.println("After compression:" + gzip.length);
        
        // Also set the header to tell the browser that this is compressed data!
        response.setHeader("content-encoding"."gzip");
        response.setContentLength(gzip.length);
        response.getOutputStream().write(gzip);
     
    }

Copy the code

test

  • Output a large text on the 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");

    }

Copy the code
  • Effect:


HTML escape filter

Simply escape the data obtained by getParameter() to complete the function.

Enhance the 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()); }}Copy the code

The filter





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

        // An enhanced request is passed in!
        chain.doFilter(myHtmlRequest, response);

    }

Copy the code

test

The JSP code:


	<form action="${pageContext.request.contextPath}/Servlet1" method="post">
	
	
	    <input type="hidden" name="username" value="

Hello I hello

"

>
<input type="submit" value="Submit"> </form> Copy the code

The Servlet code:


    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String value = request.getParameter("username");
        response.getWriter().write(value);

    }

Copy the code


Cache data into memory

As we did earlier, let the browser not cache data (captcha images should not be cached).

Now what we need to do is: cache data into memory.

This is very similar to the Filter for compressing data, in that instead of exporting the data directly to the browser, the data is stored in a container (ByteArrayOutputSteam). If there’s already a cache, take the cached one. Execute target resource without cache!

Enhancing the Response object

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(a) throws IOException {

        // This ServletOutputSteam object writes data to byteArrayOutputSteam when it calls write()
        return new MyServletOutputStream(byteArrayOutputStream);
    }

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

        return printWriter;
    }

    public byte[] getBuffer() {

        try {

            // To prevent data from being cached, refresh!
            if(printWriter ! =null) {
                printWriter.close();
            }
            if(byteArrayOutputStream ! =null) {
                byteArrayOutputStream.flush();
                returnbyteArrayOutputStream.toByteArray(); }}catch (IOException e) {
            e.printStackTrace();
        }
        return null; }}// Enhance the ServletOutputSteam so that the Writer method does not return data directly to the browser

class MyServletOutputStream extends ServletOutputStream {

    private ByteArrayOutputStream byteArrayOutputStream;

    public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
        this.byteArrayOutputStream = byteArrayOutputStream;
    }

    // When the write() method is called, data is actually written to byteArrayOutputSteam
    @Override
    public void write(int b) throws IOException {
        this.byteArrayOutputStream.write(b); }}Copy the code

The filter



    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        // Define a Map set. Key is the address of the page and value is the memory cache
        Map<String, byte[]> map = new HashMap<>();

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        // Get the resource the client wants to request
        String uri = request.getRequestURI();
        byte[] bytes = map.get(uri);

        // If there is a cache, just return it to the browser, no need to execute the target resource
        if(bytes ! =null) {
            response.getOutputStream().write(bytes);
            return ;
        }

        // If there is no cache, let the target execute
        MyResponse myResponse = new MyResponse(response);
        chain.doFilter(request, myResponse);

        // Get the data that the target resource wants to send to the browser
        byte[] b = myResponse.getBuffer();

        // Put the data into the collection
        map.put(uri, b);

        // Return the data to the browser
        response.getOutputStream().write(b);


    }

Copy the code

test

Even though it’s a refresh, you get the data from the cache!


If the article has the wrong place welcome to correct, everybody exchanges with each other. Students who are used to reading technical articles on wechat can follow the wechat public account :Java3y