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

@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

设计到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();
}

Reference

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

posted @ 2018-02-26 10:56  默默的看雨下  阅读(331)  评论(0编辑  收藏  举报