2.1 Java Servlet 会话管理 通过URL重写

URL重写

URL重写是一种会话跟踪技术,它将一个或多个键值对 添加到URL的查询字符串中,每个键值对 通常为 key=value形式,格式如下所示:

1
url?key_1=value_1&key_2=value_2...&key_n=value_n

注意,URL键值对之间用问号?来分割,键值对之间用单与符号&分割。
URL重写适合于在少数URL之间传递信息的情况下,它有如下限制:

  • URL在某些浏览器上最大长度为2000字符,所以URL重写可能无法传递大量数据。
  • 如果要传递信息到下一个资源,需要将信息插入到URL的查询字符串中,这种情况适用于动态页面之间传值,而不适用于静态页面之间传值;
  • URL重写需要在服务端上完成,所有的链接都必须带值,因此当一个页面存在很多链接时,其处理过程会是一个不小的挑战;
  • 对应信息中的某些字符,例如空格、单与符号&和问号?等必须用base64编码,而编码和解码会增加开销;
  • URL的内容会显示在浏览器的地址栏上,所以URL重写不适合用来传递敏感信息。

所以,**URL重写仅适合于在少量页面间 传递不敏感信息的情况。**
下面来写个Demo,实用URL重写在同一个页面中传递信息。

Demo

项目目录

穿件一个名为SessionManagement的动态Java Web项目,然后创建项目结果如下所示:
这里有一张图片

完整代码

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package session.management;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//配置Servlet映射到URL
@WebServlet(
name = "RewriteURL",
// 为一个servlet匹配多个URL
urlPatterns ={"/test"}
)
public class RewriteURL extends HttpServlet
{
private static final long serialVersionUID = 1L;
private ArrayList<String> fruits = null;
private ArrayList<String> vegetables = null;
// 初始化数据
@Override
public void init() throws ServletException
{
fruits = new ArrayList<String>(5);
fruits.add("苹果");
fruits.add("香蕉");
fruits.add("草莓");
fruits.add("西瓜");
fruits.add("龙眼");
vegetables = new ArrayList<String>(5);
vegetables.add("白菜");
vegetables.add("紫菜");
vegetables.add("菠菜");
vegetables.add("南瓜");
vegetables.add("冬瓜");
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// 获取查询字符串
String choice = request.getParameter("choice");
//
if (choice == null) {
showIndex(response);
} else if ("fruits".equals(choice)) {
//如果用户选择了水果的显示水果
showFruits(response);
} else if ("vegetables".equals(choice)) {
//如果用户选择了蔬菜的显示蔬菜
showVegetables(response);
}
}
private void showIndex(HttpServletResponse response)
{
// 设置响应的格式为HTML
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
// 获取响应输出
PrintWriter writer;
try {
writer = response.getWriter();
String htmlResponse =
"<html>\r\n" +
" <head>\r\n"+
" <meta charset=\"utf-8\">\r\n"+
" <title>测试URL重写</title>\r\n"+
" </head>\r\n"+
" <body>\r\n"+
" <hr>\r\n" +
" <br><br>"+
" <hr>\r\n"+
" <a href=\"?choice=fruits\">显示水果</a>&nbsp;\r\n"+
" <a href=\"?choice=vegetables\">显示蔬菜</a>\r\n"+
" </body>\r\n" +
"</html>";
// 输出响应
writer.write(htmlResponse);
} catch (IOException e) {
e.printStackTrace();
}
}
private void showFruits(HttpServletResponse response)
{
// 设置响应的格式为HTML
response.setContentType("text/html");
//因为响应带有中文,设置编码格式为中文,
//不然会出现中文乱码??????这种情况。
response.setCharacterEncoding("UTF-8");
PrintWriter writer;
try {
// 获取响应输出
writer = response.getWriter();
// 生成水果列表
String ul = "";
for (String string : fruits) {
ul += "<li>" + string + "</li>";
}
//拼接成完整的HTML内容
String responseHTML =
"<html>\r\n" +
" <head>\r\n"+
" <meta charset=\"utf-8\">\r\n"+
" <title>测试URL重写</title>\r\n" +
" </head>\r\n"+
" <body>\r\n" +
" <a href=\"test\">返回首页</a>"+
" <hr>\r\n" + ul +
" <hr>\r\n"+
" <a href=\"?choice=fruits\">显示水果</a>&nbsp;\r\n"+
" <a href=\"?choice=vegetables\">显示蔬菜</a>\r\n"+
" </body>\r\n" +
"</html>";
// 输出响应对象中
writer.write(responseHTML);
//Servlet容器被把响应对象中的响应发给浏览器,我们不用管
} catch (IOException e) {
e.printStackTrace();
}
}
private void showVegetables(HttpServletResponse response)
{
// 设置响应的格式为HTML
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
// 获取响应输出
PrintWriter writer;
try {
writer = response.getWriter();
String ul = "";
for (String string : vegetables) {
ul += "<li>" + string + "</li>";
}
String responseHTML =
"<html>\r\n" +
" <head>\r\n"+
" <meta charset=\"utf-8\">\r\n"+
" <title>测试URL重写</title>\r\n" +
" </head>\r\n"+
" <body>\r\n" +
" <a href=\"test\">返回首页</a>"+
" <hr>\r\n" + ul +
" <hr>\r\n"+
" <a href=\"?choice=fruits\">显示水果</a>&nbsp;\r\n"+
" <a href=\"?choice=vegetables\">显示蔬菜</a>\r\n"+
" </body>\r\n" +
"</html>";
// 输出响应
writer.write(responseHTML);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

代码详解

URL映射

Servlet中的URL映射的注解如下所示:

1
2
3
4
5
6
//配置Servlet映射到URL
@WebServlet(
name = "RewriteURL",
// 为一个servlet匹配多个URL
urlPatterns ={"/test"}
)

name表示当前session.management.RewriteURL这个Servlet的部署名。
urlPatterns表示该ServletURL地址,这个是个数字形式,同一个Servlet可以设置不同的URL地址.
映射好URL后,后面就可以通过http://localhost:8080/项目名称/test这样的格式来访问这个Servlet,这里是:
http://localhost:8080/SessionManagement/test

初始化数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private ArrayList<String> fruits = null;
private ArrayList<String> vegetables = null;
// 初始化数据
@Override
public void init() throws ServletException
{
fruits = new ArrayList<String>(5);
fruits.add("苹果");
fruits.add("香蕉");
fruits.add("草莓");
fruits.add("西瓜");
fruits.add("龙眼");
vegetables = new ArrayList<String>(5);
vegetables.add("白菜");
vegetables.add("紫菜");
vegetables.add("菠菜");
vegetables.add("南瓜");
vegetables.add("冬瓜");
}

在这个Servlet中有两个成员变量fruitsvegetables用于存放要展示的数据。
当用户第一次通过URL访问到该Servelet时,Web容器(Tomcat)会调用init()方法来初始化数据。在这里,init()方法用来初始化成员变量的数据。

响应get请求 覆盖doGet方法

当直接通过http://localhost:8080/SessionManagement/test这个URL访问的时候,Tomcat会调用init()方法来初始化,初始化只进行一次.后面不再调用init()方法.然后调用doGet()方法来响应请求。doGet方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// 获取查询字符串
String choice = request.getParameter("choice");
//
if (choice == null) {
showIndex(response);
} else if ("fruits".equals(choice)) {
//如果用户选择了水果的显示水果
showFruits(response);
} else if ("vegetables".equals(choice)) {
//如果用户选择了蔬菜的显示蔬菜
showVegetables(response);
}
}

doGet方法会获取请求中名称为choice的参数值。然后根据参数值的不同来调用不同的方法进行响应。
当我们请求http://localhost:8080/SessionManagement/test对应的Servlet时,URL中没有带查询字符串,所以不存在choice这个参数。request.getParameter("choice");返回null.这样就会调用showIndex(response);这个方法来响应。

返回初始界面

showIndex()方法如下,所谓的响应,就是往响应对象里写一些字符串。Web容器会把响应对象中的这些字符串处理一下然后发送给浏览器。

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
private void showIndex(HttpServletResponse response)
{
// 设置响应的格式为HTML
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
// 获取响应输出
PrintWriter writer;
try {
writer = response.getWriter();
String htmlResponse =
"<html>\r\n" +
" <head>\r\n"+
" <meta charset=\"utf-8\">\r\n"+
" <title>测试URL重写</title>\r\n" +
" </head>\r\n"+
" <body>\r\n"+
" <hr>\r\n" +
" <br><br>"+
" <hr>\r\n"+
" <a href=\"?choice=fruits\">显示水果</a>&nbsp;\r\n"+
" <a href=\"?choice=vegetables\">显示蔬菜</a>\r\n"+
" </body>\r\n" +
"</html>";
// 输出响应
writer.write(htmlResponse);
} catch (IOException e) {
e.printStackTrace();
}
}

此时浏览器显示如下:
这里有一张图片
这个页面有两个超链接,如下所示:

1
2
<a href="?choice=fruits">显示水果</a>
<a href="?choice=vegetables">显示蔬菜</a>

响应重写的URL

在刚才的页面中,显示水果的URL为?choice=fruits这是个查询字符串,查询字符串中有一个名为choice的参数,参数值为fruits
如果点击这超链接,浏览器会再次发送一个HTTP请求给服务器。
这里URL中地址缺省,浏览器会默认发给当前的地址。也就是在http://localhost:8080/SessionManagement/test这个地址后面加上查询字符串,得到的URL为:http://localhost:8080/SessionManagement/test?choice=fruits,这相当于重写了URL.

虽然,这个时候请求的Servlet还是http://localhost:8080/SessionManagement/test这个URL对应的Servlet.但是这回跟第一次请求时的情况是不一样的,第一次没有带请求参数,这次带了请求参数。Tomcat还是会调用doGet来生成响应。
不过这次因为带了参数choice=fruits,doGet方法会调用showFruits方法来生成响应。

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
private void showFruits(HttpServletResponse response)
{
// 设置响应的格式为HTML
response.setContentType("text/html");
//因为响应带有中文,设置编码格式为中文,不然会出现中文乱码??????这种情况。
response.setCharacterEncoding("UTF-8");
PrintWriter writer;
try {
// 获取响应输出
writer = response.getWriter();
// 生成水果列表
String ul = "";
for (String string : fruits) {
ul += "<li>" + string + "</li>";
}
//拼接成完整的HTML内容
String responseHTML =
"<html>\r\n" +
" <head>\r\n"+
" <meta charset=\"utf-8\">\r\n"+
" <title>测试URL重写</title>\r\n" +
" </head>\r\n"+
" <body>\r\n" +
" <hr>\r\n" + ul +
" <hr>\r\n"+
" <a href=\"?choice=fruits\">显示水果</a>&nbsp;\r\n"+
" <a href=\"?choice=vegetables\">显示蔬菜</a>\r\n"+
" </body>\r\n" +
"</html>";
// 输出响应对象中
writer.write(responseHTML);
//Servlet容器被把响应对象中的响应发给浏览器,我们不用管
} catch (IOException e) {
e.printStackTrace();
}
}

这个方法会把水果列表中的数据输出。如下所示:
这里有一张图片
同理如果此时点击显示蔬菜链接我们就可以显示蔬菜。