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
@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)获取请求数据。
@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数据
@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信息来进行读取文件。
@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请求数据解码。
首先看请求数据的编解码:
graph LR
浏览器编码-->网络传输
网络传输-->服务器解码
不同的乱码代表不同的意思:
- %E4%B8%AD%E5%9B%BD:代表服务器没有解码数据。
- ??????:代表服务器与浏览器的编解码规范不一致。
Google Chrome在55版本以后不能手动设置编码格式,在我本地电脑上Google Chrome使用的是UTF-8编码格式进行数据编码,所以我们需要在服务器设置和浏览器一致的编码格式。在Servlet中设计到编码主要在下面两个地方:
- Request URL:http://localhost:8080/request/characterEncoding/中国?key=中国
- Request Body
Request URL
设计到Request URL,Servlet规范中我没有找到其默认的解码格式,在Tomcat 9.0.2中默认是UTF-8编码格式。在Tomcat中可以使用如下配置修改Request URL的解码格式
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" redirectPort="8443"
URIEncoding="GBK"/>
Request Body
Servlet规格中默认使用"ISO-8859-1"作为解码规范,我们可以使用setCharacterEncoding()API来进行设置解码规范。
@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编码格式
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" redirectPort="8443"
useBodyEncodingForURI="GBK"/>
我们也可以使用Filter配置所有的请求解码格式。例如:在Spring Boot中会默认注入一个CharacterEncodingFilter进行设置
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()方法进行修改。
@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();
}