SpringMVC-快速入门(6.2)- 自定义过滤器

概述

由前一节,我们知道过滤器依赖于 servlet 容器。有时候我们需要在一个请求到达 Controller 之前能够截获其请求,并且根据其具体情况对 HttpServletRequest 中的参数进行过滤或者修改。比如:过滤低俗文字、危险字符等。

那我们就需要了解下ServletRequest、HttpServletRequest、ServletRequestWrapper以及HttpServletRequestWrapper这几个接口或者类之间的层次关系。

SpringMVC-快速入门(6.2)- 自定义过滤器_第1张图片

ServletRequest 是基接口。
HttpServletRequest 继承 ServletRequest
ServletRequestWrapper 实现 ServletRequest
HttpServletRequestWrapper 实现 HttpServletRequest ,又继承 了 ServletRequestWrapper。

具体UML简图:
SpringMVC-快速入门(6.2)- 自定义过滤器_第2张图片
是不是很熟悉,标准的装饰模式
SpringMVC-快速入门(6.2)- 自定义过滤器_第3张图片

  • 抽象组件:ServletRequest
  • 具体构件(被修饰者):HttpServletRequest
  • 抽象装饰类:ServletRequestWrapper
  • 具体装饰类:HttpServletRequestWrapper

那怎么如何在 Filter 中修改后台 Controller 中获取到的 HttpServletRequest 中的参数?

只需要在 Filter 中自定义一个类继承于 HttpServletRequestWrapper,并重写 getParameterNames、getParameter、getParameterValues 等方法即可。

1、修改HttpServletRequest的参数

1.1 新增装饰类ModifyParametersWrapper

新增装饰类 ModifyParametersWrapper,继承于 HttpServletRequestWrapper,(复用HttpServletRequestWrapper既有功能),用于获取请求参数。

package com.ymqx.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;

public class ModifyParametersWrapper extends HttpServletRequestWrapper {

    private Map<String, String[]> parameterMap; // 所有参数的Map集合
    /**
     * 继承HttpServletRequestWrapper,创建装饰类,以达到修改HttpServletRequest参数的目的
     */
    public ModifyParametersWrapper(HttpServletRequest request) {
        super(request);
        parameterMap = request.getParameterMap();
    }

    // 重写几个HttpServletRequestWrapper中的方法
    /**
     * 获取所有参数名
     *
     * @return 返回所有参数名
     */
    @Override
    public Enumeration<String> getParameterNames() {
        Vector<String> vector = new Vector<String>(parameterMap.keySet());
        return vector.elements();
    }

    /**
     * 获取指定参数名的值,如果有重复的参数名,则返回第一个的值 接收一般变量 ,如text类型
     *
     * @param name
     *            指定参数名
     * @return 指定参数名的值
     */
    @Override
    public String getParameter(String name) {
        String[] results = parameterMap.get(name);
        if (results == null || results.length <= 0)
            return null;
        else {
            System.out.println("修改之前: " + results[0]);
            return modify(results[0]);
        }
    }

    /**
     * 获取指定参数名的所有值的数组,如:checkbox的所有数据
     * 接收数组变量 ,如checkobx类型
     */
    @Override
    public String[] getParameterValues(String name) {
        String[] results = parameterMap.get(name);
        if (results == null || results.length <= 0)
            return null;
        else {
            int length = results.length;
            for (int i = 0; i < length; i++) {
                System.out.println("修改之前2: " + results[i]);
                results[i] = modify(results[i]);
            }
            return results;
        }
    }

    /**
     * 自定义的一个简单修改原参数的方法,即:给原来的参数值前面添加了一个修改标志的字符串
     *
     * @param string
     *            原参数值
     * @return 修改之后的值
     */
    private String modify(String string) {
        return "Modified: " + string;
    }
}

新增 ModifyParametersWrapper,然后复写了 ServletRequest 中的几个方法,具体来说就是将原来的每个参数的值的前面加上了 “Modified: ”这个字符串。

1.2 新增过滤器MyFilter

新增过滤器 MyFilter ,继承于 OncePerRequestFilter,重写 doFilterInternal() 方法,将装饰类 ModifyParametersWrapper 加入 filterChain

package com.ymqx.filter;

import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyFilter extends OncePerRequestFilter   {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ModifyParametersWrapper mParametersWrapper = new ModifyParametersWrapper(request);
        filterChain.doFilter(mParametersWrapper, response);
    }
}

public void doFilter ( ServletRequest request, ServletResponse response );

方法中第一个参数是 ServletRequest,而 ModifyParametersWrapper 继承 HttpServletRequestWrapper ,而 HttpServletRequestWrapper 实现 ServletRequest 。
最终传参ModifyParametersWrapper 对象,会调用ModifyParametersWrapper 自定义逻辑方法。

1.3 在web.xml中注册该过滤器


  <filter>
    <filter-name>MyFilterfilter-name>
    <filter-class>com.ymqx.filter.MyFilterfilter-class>
  filter>
  <filter-mapping>
    <filter-name>MyFilterfilter-name>
    <url-pattern>/filter/*url-pattern>
    
    <dispatcher>REQUESTdispatcher>
    <dispatcher>FORWARDdispatcher>
  filter-mapping>

也可以通过注解方式声明:

@WebFilter(filterName = “MyFilter”, urlPatterns = {“/filter/*”})
public class MyFilter extends OncePerRequestFilter {

}

1.4 新增一个测试使用的Controller

FilterController :

package com.ymqx.controller;

import com.ymqx.pojo.Product;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/filter")
public class FilterController {
    @RequestMapping("/action1")
    public void action1(@RequestParam("name") String name){
        System.out.println("修改之后: " + name);
    }
}

执行 http://localhost:8080/filter/action1?name=123 ,结果如下:

修改之前2123
修改之后: Modified: 123

这就表明了我们前面自定义的过滤器已经将 HttpServletRequest 中原来的参数成功修改了。

同时,还说明SpringMVC的 @RequestParam 注解本质上调用的是ServletRequest中的 getParameterValues(String name) 方法,而不是getParameter(String name) 方法。

其实装饰类 ModifyParametersWrapper 只用到了构造方法 ModifyParametersWrapper()getParameterValues() 方法,简化装饰类ModifyParametersWrapper:

package com.ymqx.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;

public class ModifyParametersWrapper extends HttpServletRequestWrapper {

    private Map<String, String[]> parameterMap; // 所有参数的Map集合
	
    /**
     * 继承HttpServletRequestWrapper,创建装饰类,以达到修改HttpServletRequest参数的目的
     */
    public ModifyParametersWrapper(HttpServletRequest request) {
        super(request);
        parameterMap = request.getParameterMap();
    }

    /**
     * 获取指定参数名的所有值的数组,如:checkbox的所有数据
     * 接收数组变量 ,如checkobx类型
     */
    @Override
    public String[] getParameterValues(String name) {
        String[] results = parameterMap.get(name);
        if (results == null || results.length <= 0)
            return null;
        else {
            int length = results.length;
            for (int i = 0; i < length; i++) {
                System.out.println("修改之前2: " + results[i]);
                results[i] = modify(results[i]);
            }
            return results;
        }
    }

    /**
     * 自定义的一个简单修改原参数的方法,即:给原来的参数值前面添加了一个修改标志的字符串
     *
     * @param string
     *            原参数值
     * @return 修改之后的值
     */
    private String modify(String string) {
        return "Modified: " + string;
    }
}

1.5 小结

  1. 新增装饰类 ModifyParametersWrapper,继承于 HttpServletRequestWrapper,重写 getParameterValues() 方法。
  2. 新增过滤器 MyFilter,继承于 OncePerRequestFilter,重写 doFilterInternal() 方法,将装饰类 ModifyParametersWrapper 加入 filterChain。
  3. web.xml中注册该过滤器。

2、修改HttpServletRequest里body数据

getInputStream() 方法会返回 HttpServletRequest 请求里 body数据,将获取的原数据修改后,用修改后的数据构造新的 InputStream 流,替换原 InputStream 流

2.1 新增装饰类ModifyParametersWrapper

新增装饰类 ModifyBodyWrapper ,继承于 HttpServletRequestWrapper,用于获取请求报文体body数据。

public class ModifyBodyWrapper extends HttpServletRequestWrapper {

    // 存放JSON数据主体
    private String body;

    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request
     * @throws IllegalArgumentException if the request is null
     */
    public ModifyBodyWrapper(HttpServletRequest request) {
        super(request);
        /* 1、获取Request的body数据 */
        String requestBody = getBody(request);
        System.out.println("修改前requestBody=" + requestBody);

        /* 2、更改Request的body数据 */
        JSONObject jsonObject = JSONObject.parseObject(requestBody);
        Set<Map.Entry<String, Object>> entries = jsonObject.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            System.out.println("key=" + entry.getKey());
            System.out.println("value=" + entry.getValue());

            if (entry.getValue() instanceof String) {
                entry.setValue(((String) entry.getValue()).trim());
            }
        }
        body = jsonObject.toJSONString();
        System.out.println("修改后requestBody=" + body);

        /* 3、重写getInputStream()方法,将修改后的RequestBody返回 */
    }

    //获取Request的body数据
    public String getBody(HttpServletRequest request){
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));

            String buffer = null;
            while (null != (buffer = bufferedReader.readLine())){
                stringBuilder.append(buffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return stringBuilder.toString();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes("UTF-8"));
        ServletInputStream servletInputStream = new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}

定义全局变量 body 存放修改后的数据。

inputStream = request.getInputStream();

调用 HttpServletRequest 的方法 getInputStream(),获取 body 请求体;然后解析报文体,进行去空格处理。

@Override
public ServletInputStream getInputStream() throws IOException {
     final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes("UTF-8"));
     ServletInputStream servletInputStream = new ServletInputStream() {

         @Override
         public int read() throws IOException {
             return byteArrayInputStream.read();
         }
     };
     return servletInputStream;
}

重写 getInputStream() 方法,将修改后的报文体 body 返回。

其实装饰类ModifyBodyWrapper 只用到了构造方法 ModifyBodyWrapper()getInputStream() 方法

2.2 新增过滤器MyFilter2

新增过滤器 MyFilter2,继承于 OncePerRequestFilter,重写 doFilterInternal() 方法,将装饰类 ModifyBodyWrapper 加入 filterChain

public class MyFilter2 extends OncePerRequestFilter   {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ModifyBodyWrapper modifyBodyWrapper = new ModifyBodyWrapper(request);
        filterChain.doFilter(modifyBodyWrapper, response);
    }
}

2.3 在web.xml中注册该过滤器

<filter>
    <filter-name>MyFilter2filter-name>
    <filter-class>com.ymqx.filter.MyFilter2filter-class>
  filter>
  <filter-mapping>
    <filter-name>MyFilter2filter-name>
    <url-pattern>/filter/*url-pattern>
    
    <dispatcher>REQUESTdispatcher>
    <dispatcher>FORWARDdispatcher>
  filter-mapping>

也可以通过注解方式声明:

@WebFilter(filterName = “MyFilter2”, urlPatterns = {“/filter/*”})
public class MyFilter2 extends OncePerRequestFilter {

}

2.4 新增一个请求映射

FilterController :

@Controller
@RequestMapping("/filter")
public class FilterController {
    @RequestMapping("/action1")
    public void action1(@RequestParam("name") String name){
        System.out.println("修改之后: " + name);
    }

    @RequestMapping("/action2")
    public void action1(@RequestBody Product product){
        System.out.println("Product:" + product);
    }
}

postman 发送报文体,地址 http://localhost:8080/filter/action2

SpringMVC-快速入门(6.2)- 自定义过滤器_第4张图片

结果如下:

修改前requestBody={"id":"1001  ","name":"李雷  ","price":"1.2"}
key=price
value=1.2
key=name
value=李雷  
key=id
value=1001  
修改后requestBody={"price":"1.2","name":"李雷","id":"1001"}
Product:编号(id)1001,名称(name):李雷,价格(price)1.2

上送报文体的内容被修改。

2.5 小结

  1. 新增装饰类 ModifyBodyWrapper,继承于 HttpServletRequestWrapper,重写 getInputStream()方法。
  2. 新增过滤器 MyFilter2,继承于 OncePerRequestFilter,重写 doFilterInternal() 方法,将装饰类 ModifyBodyWrapper 加入 filterChain。
  3. web.xml中注册该过滤器。

3、同时修改参数、body数据

为了打印简洁,去除装饰类ModifyParametersWrapper、ModifyBodyWrapper不必要打印输出,同时将两个过滤器注册到web.xml中。


  <filter>
    <filter-name>MyFilterfilter-name>
    <filter-class>com.ymqx.filter.MyFilterfilter-class>
  filter>
  <filter-mapping>
    <filter-name>MyFilterfilter-name>
    <url-pattern>/filter/*url-pattern>
    
    <dispatcher>REQUESTdispatcher>
    <dispatcher>FORWARDdispatcher>
  filter-mapping>

  <filter>
    <filter-name>MyFilter2filter-name>
    <filter-class>com.ymqx.filter.MyFilter2filter-class>
  filter>
  <filter-mapping>
    <filter-name>MyFilter2filter-name>
    <url-pattern>/filter/*url-pattern>
    
    <dispatcher>REQUESTdispatcher>
    <dispatcher>FORWARDdispatcher>
  filter-mapping>

控制器FilterController:

@Controller
@RequestMapping("/filter")
public class FilterController {
    ...
    @RequestMapping("/action3")
    public void action3(@RequestParam("name") String name, @RequestBody Product product){
        System.out.println("修改之后: " + name);
        System.out.println("Product:" + product);
    }
}

postman 发送报文体,地址 http://localhost:8080/filter/action3?name=lili

SpringMVC-快速入门(6.2)- 自定义过滤器_第5张图片

结果如下:

修改前requestBody={"id":"1001  ","name":"李雷  ","price":"1.2"}
修改后requestBody={"price":"1.2","name":"李雷","id":"1001"}
修改之后: Modified: lili
Product:编号(id)1001,名称(name):李雷,价格(price)1.2

参考文章:
springmvc总结(配置传递参数去除前后空格、参数绑定时处理日期)
使用filter拦截参数去掉两端的空格
如何获取HttpServletRequest里body数据

你可能感兴趣的:(SpringMVC,springmvc)