一 简介

如题所示,有时候我们需要在一个请求到达Controller之前能够截获其请求,并且根据其具体情况对 HttpServletRequest 中的参数进行过滤或者修改。这时,有的同学可能会想:我们是否可以在一个Filter中将 HttpServletRequest 里的所有参数都取出来分别进行过滤然后再放回到该HttpServletRequest 中呢?

很显然,在 HttpServletRequest 貌似只有 setAttribute(String name, Object o) 这个方法可以设置参数,但是我们经过尝试之后可以发现:使用 setAttribute(String name, Object o) 方法来重新设置参数显然是不行的,因为在Controller中获取参数本质上还是调用的ServletRequest的public String getParameter(String name) 或者 public String[] getParameterValues(String name) 方法,因此想要达到“在Filter中修改HttpServletRequest的参数”的目的,显然是需要使用装饰模式来复写这些方法才行的

在正式代码之前,我还是先简单介绍下ServletRequest、HttpServletRequest、ServletRequestWrapper以及HttpServletRequestWrapper这几个接口或者类之间的层次关系,并说明“继承HttpServletRequestWrapper类以实现在Filter中修改HttpServletRequest的参数”这种方式的原理是什么

如果我们从网上下载tomcat的源代码并查看的话,就可以很清楚地看到这几个类之间的层次关系了,在eclipse中看,它们之间的层次关系是这样的:

继承HttpServletRequestWrapper以实现在Filter中修改HttpServletRequest的参数

如果这个图表还不够清楚地话,我还画了一个简单的UML结构图:

继承HttpServletRequestWrapper以实现在Filter中修改HttpServletRequest的参数

注:因为我现在没有下载专门的UML建模工具,因此就使用“画图”工具简单地画了一下类图,同时这里的ModifyParametersWrapper 是我后面举例用到的的一个自定义的类

如果学过“装饰模式”的童鞋可能已经发现了,上面这个关系毫无疑问是一个很标准的装饰模式:

  • ServletRequest    抽象组件
  • HttpServletRequest    抽象组件的一个子类,它的实例被称作“被装饰者”
  • ServletRequestWrapper    一个基本的装饰类,这里是非抽象的
  • HttpServletRequestWrapper    一个具体的装饰者,当然这里也继承了HttpServletRequest这个接口,是为了获取一些在ServletRequest中没有的方法
  • ModifyParametersWrapper    同样是 一个具体的装饰者(PS:我自定义的一个类)

注:一个标准的装饰模式的UML类图是这样的:

 

 

继承HttpServletRequestWrapper以实现在Filter中修改HttpServletRequest的参数

那么问题来了,如何在Filter中修改后台Controller中获取到的HttpServletRequest中的参数?

答:很简单,只需要在Filter中自定义一个类继承于HttpServletRequestWrapper,并复写getParameterNames、getParameter、getParameterValues等方法即可

二 代码实现

(1)自定义的过滤器ModifyParametersFilter.java:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

package cn.zifangsky.filter;

 

import java.io.IOException;

import java.util.Enumeration;

import java.util.Map;

import java.util.Vector;

 

import javax.servlet.FilterChain;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletRequestWrapper;

import javax.servlet.http.HttpServletResponse;

 

import org.springframework.web.filter.OncePerRequestFilter;

 

public class ModifyParametersFilter extends OncePerRequestFilter {

 

    @Override

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)

            throws ServletException, IOException {

        ModifyParametersWrapper mParametersWrapper = new ModifyParametersWrapper(request);

        filterChain.doFilter(mParametersWrapper, response);

    }

 

    /**

     * 继承HttpServletRequestWrapper,创建装饰类,以达到修改HttpServletRequest参数的目的

     */

    private class ModifyParametersWrapper extends HttpServletRequestWrapper {

        private Map<String, String[]> parameterMap; // 所有参数的Map集合

 

        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: ”这个字符串

(2)在web.xml中注册该过滤器:

 

1

2

3

4

5

6

7

8

9

10

11

    <filter>

        <filter-name>ModifyParametersFilter</filter-name>

        <filter-class>cn.zifangsky.filter.ModifyParametersFilter</filter-class>

    </filter>

    <filter-mapping>

        <filter-name>ModifyParametersFilter</filter-name>

        <url-pattern>/param/*</url-pattern>

        <!-- 直接从客户端过来的请求以及通过forward过来的请求都要经过该过滤器 -->

        <dispatcher>REQUEST</dispatcher>

        <dispatcher>FORWARD</dispatcher>

    </filter-mapping>

(3)添加一个测试使用的Controller,即:TestModifyController.java:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

package cn.zifangsky.controller;

 

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

 

@Controller

public class TestModifyController {

    

    @RequestMapping("/param/modify.html")

    public void modify(@RequestParam("name") String name){

        System.out.println("修改之后: " + name);

    }

}

这里没有处理复杂的逻辑,仅仅只是简单地输出

(4)测试:

启动项目之后访问:http://localhost:9180/FilterDemo/param/modify.html?name=abc

可以发现,在控制台中输出如下:

 

1

2

  修改之前2: abc

修改之后: Modified: abc

这就表明了我们前面自定义的过滤器已经将HttpServletRequest中原来的参数成功修改了。同时,还说明SpringMVC的@RequestParam注解本质上调用的是ServletRequest中的 getParameterValues(String name) 方法而不是 getParameter(String name) 方法

注:tomcat-8.5.5源码下载地址:http://pan.baidu.com/s/1skWOso9

相关文章:

  • 2021-06-09
  • 2022-12-23
  • 2021-05-26
  • 2021-07-22
  • 2021-05-26
  • 2021-12-09
  • 2021-07-26
  • 2022-01-07
猜你喜欢
  • 2022-12-23
  • 2021-07-03
  • 2022-12-23
  • 2021-08-30
  • 2021-05-26
  • 2021-12-26
相关资源
相似解决方案