【问题标题】:JSP not showing correct UTF-8 contents for HTML form POSTJSP 没有为 HTML 表单 POST 显示正确的 UTF-8 内容
【发布时间】:2019-06-03 08:00:16
【问题描述】:

我将 Java 11 与具有最新 JSP/JSTL 的 Tomcat 9 结合使用。我正在 Windows 10 上的 Chrome 71 和 Firefox 64.0 中进行测试。我有以下测试文档:

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="en-US">
<head>
  <meta charset="UTF-8"/>
  <title>Hello</title>
</head>
<body>
  <c:if test="${not empty param.fullName}">
    <p>Hello, ${param.fullName}.</p>
  </c:if>

  <form>
    <div>
      <label>Full name: <input name="fullName" /></label>
    </div>
    <button>Say Hello</button>
  </form>
</body>
</html>

这可能是最简单的形式。如您所知,method 表单默认为getaction 表单默认为""(提交到同一页面),enctype 表单默认为application/x-www-form-urlencoded

如果我在字段中输入名称“Flávio José”(巴西著名的 forró 歌手和音乐家)并提交,则表单将通过 HTTP GET 提交到使用 hello.jsp?fullName=Fl%C3%A1vio+Jos%C3%A9 的同一页面。这是正确的,页面显示:

Hello, Flávio José.

如果我将表单 method 更改为 post 并输入相同的名称“Flávio José”,则表单内容将通过 POST 提交,并带有 HTTP 请求内容:

fullName=Fl%C3%A1vio+Jos%C3%A9

这看起来也是正确的。但这次页面显示:

Hello, Flávio José.

JSP 似乎认为这些是一系列 ISO-8859-1 八位字节(或代码页 1252 八位字节),而不是将 %C3%A 视为一个 UTF-8 八位字节序列,因此将它们解码为错误字符序列。

但是它从哪里获得 ISO-8859-1?我的 JSP 页面缺少什么来指示正确的编码?

我还要注意WHATWG specification 表示application/x-www-form-urlencoded 八位字节默认应被解析为 UTF-8。 Java servlet 规范是否被破坏了?我该如何解决这个问题?

【问题讨论】:

  • HTTP请求的Content-Type是否指定了字符集?

标签: java forms jsp tomcat servlets


【解决方案1】:

这是由Tomcat引起的,但根本问题是Java Servlet 4规范不正确且过时。

最初的 HTML 4.0.1 说 application/x-www-form-urlencoded 编码的八位字节应该是 decoded as US-ASCII。 servlet 规范将其更改为,如果未指定请求编码,则八位字节应解码为 ISO-8859-1。 Tomcat 只是遵循 servlet 规范。

Java servlet 规范存在两个问题。首先是application/x-www-form-urlencoded 的现代解释是编码八位字节should be decoded using UTF-8。第二个问题是将八位位组解码与资源字符集联系在一起会混淆两个解码级别。

再看看这个POST的内容:

fullName=Fl%C3%A1vio+Jos%C3%A9

你会注意到它是 ASCII !如果您将POST HTTP 请求字符集视为ISO-8859-1UTF-8US-ASCII,这并不重要——您仍然会得到完全相同的Unicode 字符之前解码八位字节!使用什么编码来解码编码字节是完全独立的。

再举一个例子,假设我下载了一个明确标记为 ISO-8859-1 的文本文件 instructions.txt,它包含 URI https://example.com/example.jsp?fullName=Fl%C3%A1vio+Jos%C3%A9。仅仅因为文本文件的字符集为ISO-8859-1,这是否意味着我需要使用 ISO-8859-1 解码%C3%A?当然不是!用于解码 URI 字符的字符集是在资源内容类型字符集之上的单独解码级别!同样,application/x-www-form-urlencoded 中编码的八位字节值应使用 UTF-8 解码,而不管资源的底层字符集如何。

有几种解决方法,其中一些是通过查看Tomcat character encoding FAQ to "use UTF-8 everywhere" 找到的。

web.xml 文件中设置请求字符编码。

将以下内容添加到您的 WEB-INF/web.xml 文件中:

<request-character-encoding>UTF-8</request-character-encoding>

此设置与 servlet 容器实现无关,并在 servlet 规范中进行了定义。 (如果需要全局设置并且不介意更改 Tomcat 配置,您应该可以将其放入 Tomcat 的 conf/web.xml 文件中。)

在您的web.xml 文件中设置SetCharacterEncodingFilter

Tomcat 有一个专有的等价物:在WEB-INF/web.xml 文件中使用org.apache.catalina.filters.SetCharacterEncodingFilter,正如上面提到的Tomcat FAQ 和https://stackoverflow.com/a/37833977/421049 所示,摘录如下:

<filter>
  <filter-name>setCharacterEncodingFilter</filter-name>
  <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>setCharacterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

这将使您的 Web 应用程序只能在 Tomcat 上运行,因此最好将其放在 Tomcat 安装 conf/web.xml 文件中,如上面的帖子所述。其实Tomcat的conf/web.xml安装有这两个部分,只是注释掉了;只需取消注释它们就可以了。

在 JSP 或 servlet 中强制请求字符编码为 UTF-8。

您可以在 JSP 早期的某处强制将 servlet 请求的字符编码为 UTF-8:

<% request.setCharacterEncoding("UTF-8"); %>

但这很丑陋、笨拙、容易出错,并且违背了现代最佳实践——不应再使用 JSP 脚本。

希望我们能够获得更新的 Java servlet 规范,以消除资源字符集与 application/x-www-form-urlencoded 八位字节解码之间的任何关系,并简单地声明 application/x-www-form-urlencoded 八位字节必须解码为 UTF-8,就像现代实践一样由最新的 W3C 和 WHATWG 规范阐明。

更新:我已使用此信息更新了Character Encoding Issues 上的 Tomcat 常见问题解答。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2011-10-26
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 2015-01-05
  • 1970-01-01
  • 1970-01-01
  • 2012-03-20
相关资源
最近更新 更多