Servlet3.1学习(二)

请求Request

HttpServletRequest代表一个Http请求,你可以从中获取header、cookie、body数据等等。

获取请求数据

根据Http协议规范,请求参数分为Query String、Request Body(Form Data、Request Payload)等。其常见请求内容的媒体类型有application/x-www-form-urlencoded、multipart/form-data、text/xml、application/json等等。

Servlet提供getParameter()、getInputStream()、getPart()等API来获取请求参数数据。

  • GET请求的Query String和POST请求Content-Type为application/x-www-form-urlencoded(Form data)的请求数据由getParameter()类API获取数据且不能使用getInputStream()类API获取数据。个人认为Servlet提供这种简便API获取请求数据,主要是因为这类数据是键值对形式的数据,可以通过key来获取value数据。
  • POST请求Content-Type为multipart/form-data的请求数据(文件上传)通过getPart()类API获取,这是Servlet3.0后启用的API,如果Servlet不支持getPart()类API,同样可以使用getInputStream()获取请求数据。
  • 其他形式的请求数据,例如POST请求JSON数据等,只能通过getInputStream()获取InputStream来自己解码数据。

GET请求获取请求数据

GET请求直接通过getParameter()类API获取请求数据,例如如下请求http://localhost/request?key=123

1
2
3
4
5
6
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String value = req.getParameter("key");
log.info("value: {}", value);
ResponseUtils.writeJson(resp, ResponseJson.ok());
}

POST请求获取请求数据

POST请求的Header有Content-Type字段,其代表该POST的Request Body数据是那种类型的数据。开发者根据不同的Content-Type类型使用不同的API进行获取数据。

POST Form Data(Content-Type是application/x-www-form-urlencoded)获取请求数据。

1
2
3
4
5
6
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String value = req.getParameter("key");
log.info("value: {}", value);
ResponseUtils.writeJson(resp, ResponseJson.ok());
}

POST Content-Type是其它类型获取请求数据,例如Json数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 使用流获取数据
BufferedInputStream inputStream = new BufferedInputStream(req.getInputStream());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int b;
while ((b = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, b);
}
String result = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
User user = JsonUtils.readFromJson(result, User.class);
CloseUtils.close(inputStream, outputStream);
ResponseUtils.writeJson(resp, ResponseJson.ok());
}

文件上传

文件上传使用POST请求且Content-Type类型为multipart/form-data时,Servlet提供特别的getPart()类API进行获取文件,注意需要为该Servlet配置Multipart-Config信息来进行读取文件。

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
@WebServlet(urlPatterns = "/request/post/file")
@MultipartConfig(location = "E://")
public class FileReqServlet extends HttpServlet {

private static final long serialVersionUID = 5245909855827709121L;

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
for (Part part : req.getParts()) {
String fileName = fileNameFromPart(part);
part.write(fileName);
}
ResponseUtils.writeJson(resp, ResponseJson.ok());
}

/**
* 从Part中获取文件名称
*
* @param part Part
* @return 文件名称
*/
private String fileNameFromPart(Part part) {
String contentDispose = part.getHeader("Content-Disposition");
// 如果有中文会可能会导致乱码,所以在这里需要编解码
String decodeStr = new String(contentDispose.getBytes(Charset.forName("GBK")), StandardCharsets.UTF_8);
// 分割字符串获取文件名
String[] split = StringUtils.trim(decodeStr).split(";");
return StringUtils.substringBetween(split[split.length - 1], "=\"", "\"");
}
}

请求数据解码

服务器解析Http请求数据时需要解码数据,可能会出现乱码问题,这里讨论一下Http请求数据解码。

首先看请求数据的编解码:

1
2
3
graph LR
浏览器编码-->网络传输
网络传输-->服务器解码

不同的乱码代表不同的意思:

  • %E4%B8%AD%E5%9B%BD:代表服务器没有解码数据。
  • ??????:代表服务器与浏览器的编解码规范不一致。

Google Chrome在55版本以后不能手动设置编码格式,在我本地电脑上Google Chrome使用的是UTF-8编码格式进行数据编码,所以我们需要在服务器设置和浏览器一致的编码格式。在Servlet中设计到编码主要在下面两个地方:

Request URL解码

设计到Request URL,Servlet规范中我没有找到其默认的解码格式,在Tomcat 9.0.2中默认是UTF-8编码格式。在Tomcat中可以使用如下配置修改Request URL的解码格式

1
2
3
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" redirectPort="8443"
URIEncoding="GBK"/>

Request Body解码

Servlet规格中默认使用”ISO-8859-1”作为解码规范,我们可以使用setCharacterEncoding()API来进行设置解码规范。

1
2
3
4
5
6
7
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.setCharacterEncoding("UTF-8");
String value = req.getParameter("key");
log.info("value: {}", value);
ResponseUtils.writeJson(resp, ResponseJson.ok());
}

在Tomcat中我们可以使用如下配置修改Request Body编码格式

1
2
3
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" redirectPort="8443"
useBodyEncodingForURI="GBK"/>

我们也可以使用Filter配置所有的请求解码格式。例如:在Spring Boot中会默认注入一个CharacterEncodingFilter进行设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CharacterEncodingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

String encoding = getEncoding();
if (encoding != null) {
if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding);
}
if (isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
}
filterChain.doFilter(request, response);
}
}

Header、Cookie

HttpServletRequest提供一些API获取Http Header、Cookie

请求路径

HttpServletRequest提供一些API获取请求的路径

  • Context Path:主要是Web服务器配置的当前Web应用的Context Path,以”/“开头,或者null
  • Servlet Path:该请求对于的Servlet的映射,以”/“开头
  • PathInfo:请求路径除了Context Path、Servlet Path以外的路径,主要是Servlet模糊匹配。以”/“开头,或者null

响应Response

HttpServletResponse代表一个Http响应,其封装了从服务器返回客户端的所有信息

响应数据编码

由上面可知,在服务器编码后,浏览器需要解码,这就的保证服务器、浏览器编解码格式必须相同。在我电脑的Google Chrome 63版本中,即使在Content-Type中设置了编码格式,浏览器也不会使用该编码格式,Chrome直接使用了UTF-8的编码格式。

在HttpServletResponse中通过getWrite()获取PrintWriter进行字符串输出需要进行编码,默认使用ISO-8859-1编码格式,我们可以使用setCharacterEncoding()方法进行修改。

1
2
3
4
5
6
7
8
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
System.out.println(resp.getCharacterEncoding());
PrintWriter writer = resp.getWriter();
writer.append("中国").flush();
}

Reference

http://aub.iteye.com/blog/763117