JSP未显示HTML表单POST的正确UTF-8内容

问题描述 投票:5回答:1

我正在将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默认为get,格式action默认为""(提交到同一页面),格式enctype默认为application/x-www-form-urlencoded

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

Hello, Flávio José.

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

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

这似乎也是正确的。但是这次页面显示:

Hello, Flávio José.

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

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

[我还要注意,WHATWG specification表示默认情况下应将application/x-www-form-urlencoded八位位组解析为UTF-8。 Java servlet规范是否被简单破坏?我该如何解决?

java forms jsp tomcat servlets
1个回答
4
投票

这是由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字符之前解码八位字节!用于解码编码八位位组的编码完全分开。

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

有几种解决方法,其中一些是通过查看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文件中。)

SetCharacterEncodingFilter文件中设置web.xml

Tomcat具有专有的等效项:使用org.apache.catalina.filters.SetCharacterEncodingFilter文件中的WEB-INF/web.xml,如上面的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。

您可以将Servlet请求的字符编码强制为UTP-8,在JSP的早期位置:

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

但是那是丑陋的,笨拙的,容易出错的,并且与现代最佳实践背道而驰,不再应该使用JSP scriptlet。

[希望我们可以获得更新的Java servlet规范,以删除资源字符集与application/x-www-form-urlencoded八位位组的解码之间的任何关系,并简单地声明application/x-www-form-urlencoded八位位组必须解码为UTF-8,正如现代实践所阐明的那样通过最新的W3C和WHATWG规范。

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

© www.soinside.com 2019 - 2024. All rights reserved.