一 简介
如题所示,有时候我们需要在一个请求到达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中看,它们之间的层次关系是这样的:
如果这个图表还不够清楚地话,我还画了一个简单的UML结构图:
注:因为我现在没有下载专门的UML建模工具,因此就使用“画图”工具简单地画了一下类图,同时这里的ModifyParametersWrapper 是我后面举例用到的的一个自定义的类
如果学过“装饰模式”的童鞋可能已经发现了,上面这个关系毫无疑问是一个很标准的装饰模式:
- ServletRequest 抽象组件
- HttpServletRequest 抽象组件的一个子类,它的实例被称作“被装饰者”
- ServletRequestWrapper 一个基本的装饰类,这里是非抽象的
- HttpServletRequestWrapper 一个具体的装饰者,当然这里也继承了HttpServletRequest这个接口,是为了获取一些在ServletRequest中没有的方法
- ModifyParametersWrapper 同样是 一个具体的装饰者(PS:我自定义的一个类)
注:一个标准的装饰模式的UML类图是这样的:
那么问题来了,如何在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