循序渐进写一个 Servlet (3) - 分别处理 GET 和 POST

Servlet(Server Applet),全称 Java Servlet,是用 Java 编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态 Web 内容。本系列将一步步地写出一个 Servlet 程序。

这篇博文将演示如何分别处理 GETPOST 请求,以及处理请求中的参数。

编写 doGet()doPost() 方法

首先把要实现的功能写好,后面才好调用不是。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 设定返回内容的MIME类型
response.setContentType("text/html");

// 设定内容以UTF-8编码
response.setCharacterEncoding("utf-8");

try (PrintWriter writer = response.getWriter()) {
// 开始输出HTML文本
writer.print("<html lang=\"en\">");
writer.print("<body>");
writer.print("<b>Response from DemoServlet</b>");
writer.print("<b>Handled by <code>doGet()</code></b>");
writer.print("</body>");
writer.print("</html>");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 设定返回内容的MIME类型
response.setContentType("text/html");

// 设定内容以UTF-8编码
response.setCharacterEncoding("utf-8");

try (PrintWriter writer = response.getWriter()) {
// 开始输出HTML文本
writer.print("<html lang=\"en\">");
writer.print("<body>");
writer.print("<b>Response from DemoServlet</b>");
writer.print("<b>Handled by <code>doPost()</code></b>");
writer.print("</body>");
writer.print("</html>");
}
}

区分 HTTP 方法

因为 servlet 是调用 service() 方法来处理请求的,所以对请求做区分也需要在 service() 方法中进行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {

HttpServletRequest httpServletRequest = (HttpServletRequest) req;
HttpServletResponse httpServletResponse = (HttpServletResponse) res;

if ("GET".equalsIgnoreCase(httpServletRequest.getMethod())) {
doGet(httpServletRequest, httpServletResponse);
} else if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) {
doPost(httpServletRequest, httpServletResponse);
} else {
// 如果请求既不是GET也不是POST
// 那么就返回HTTP 501 NOT IMPLEMENTED状态码
// 毕竟不能把请求直接扔了,总是要有个返回的
httpServletResponse.sendError(501);
}
}

运行起来看看效果

首先发个 GET 请求

Handling GET request

再发个 POST 请求

Handling POST request

为什么不用 HttpServlet 类呢

没错,上面做的,就是自己实现了一个简陋的 HttpServlet 类,因为是循序渐进嘛,没头没脑的直接砸上来一个,算什么循序渐进。

那么现在就让 DemoServlet 继承 HttpServlet。同时,因为 HttpServlet 已经在 service() 方法中实现了判断请求类型,所以 DemoServlet 中不要覆盖 service() 方法,只覆盖 doGet()doPost() 方法。

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
public class DemoServlet extends HttpServlet {

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 设定返回内容的MIME类型
response.setContentType("text/html");

// 设定内容以UTF-8编码
response.setCharacterEncoding("utf-8");

try (PrintWriter writer = response.getWriter()) {
// 开始输出HTML文本
writer.print("<html lang=\"en\">");
writer.print("<body>");
writer.print("<b>Response from DemoServlet</b>");
writer.print("<b>Handled by <code>doGet()</code></b>");
writer.print("</body>");
writer.print("</html>");
}
}

@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 设定返回内容的MIME类型
response.setContentType("text/html");

// 设定内容以UTF-8编码
response.setCharacterEncoding("utf-8");

try (PrintWriter writer = response.getWriter()) {
// 开始输出HTML文本
writer.print("<html lang=\"en\">");
writer.print("<body>");
writer.print("<b>Response from DemoServlet</b>");
writer.print("<b>Handled by <code>doPost()</code></b>");
writer.print("</body>");
writer.print("</html>");
}
}
}

处理请求中的参数

HTTP 请求是可以带参数的,有了参数,那就得处理。

处理 GET 请求的参数

GET 请求里带的参数,名字叫查询字符串(query string),是一组或多组 key=value 格式的键值对。

Query string 写在 URL 后面,以一个问号起头,用 & 分隔各个键值对,即类似 http://localhost:8080/appname/servlet?arg1=value1&arg2=value2&...&argN=valueN

在代码里使用 HttpServletRequest#getQueryString() 方法,就可以获取到问号后面的 query string,分别用 &= 分割字符串,就可以取到每个参数的 key 和 value。

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
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 设定返回内容的MIME类型
response.setContentType("text/html");

// 设定内容以UTF-8编码
response.setCharacterEncoding("utf-8");

/*

String queryString = request.getQueryString();
String[] queryStrings;

if (queryString != null) {
queryStrings = queryString.split("&");
} else {
queryStrings = new String[]{};
}

*/

// 使用Optional类简化null判断
Optional<String> optionalQueryString = Optional.ofNullable(request.getQueryString());

// 如果有query string则取出每个参数
// 如果没有则返回一个空的String数组
String[] queryStrings = optionalQueryString.isPresent() ? optionalQueryString.get().split("&") : new String[]{};

try (PrintWriter writer = response.getWriter()) {
// 开始输出HTML文本
writer.print("<html lang=\"en\">");
writer.print("<body>");
writer.print("<b>Response from DemoServlet</b>");
writer.print("<b>Handled by <code>doGet()</code></b>");

writer.print("<br>");

// 遍历每个参数
for (String query : queryStrings) {
// 取出参数的key和value
String[] q = query.split("=");

writer.print(q[0] + " = " + q[1] + "<br>");
}

writer.print("<br>");

writer.print("</body>");
writer.print("</html>");
}
}

运行一下,结果是这样子的:

Handling query string

处理 POST 请求的参数

POST 请求的参数就叫参数 (parameter),位于请求体 (body) 里,格式由 Content-Type 请求头决定。详细介绍可以参考这篇 MDN 文档

HttpServletRequest#getParameterMap() 方法可以取出请求中的所有参数,并放到一个 Map 中。

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
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 设定返回内容的MIME类型
response.setContentType("text/html");

// 设定内容以UTF-8编码
response.setCharacterEncoding("utf-8");

// 取出所有参数,得到一个Map
Map parameterMap = request.getParameterMap();

try (PrintWriter writer = response.getWriter()) {
// 开始输出HTML文本
writer.print("<html lang=\"en\">");
writer.print("<body>");
writer.print("<b>Response from DemoServlet</b>");
writer.print("<b>Handled by <code>doPost()</code></b>");

writer.print("<br>");

// 遍历parameterMap
parameterMap.forEach((k, v) -> writer.print(k + " = " + ((String[]) v)[0] + "<br>"));

writer.print("</body>");
writer.print("</html>");
}
}

本文中将使用 application/x-www-form-urlencoded 格式做示例。

Handling parameter

系列博文