为什么 'ServletContext#setRequestCharacterEncoding' 对 'HttpServletRequest#getReader' 没有影响?
Why 'ServletContext#setRequestCharacterEncoding' does not have an effect on 'HttpServletRequest#getReader'?
我们可以通过ServletContext#setRequestCharacterEncoding
(自 Servlet 4.0 起)设置用于读取请求主体的默认字符编码。
我认为 HttpServletRequest#getReader
的字符编码可以使用 ServletContext#setRequestCharacterEncoding(*)
设置。
但是 HttpServletRequest#getReader
returns 的 reader 似乎没有使用 ServletContext#setRequestCharacterEncoding
设置的编码解码字符。
我的问题是:
- 为什么
ServletContext#setRequestCharacterEncoding
对HttpServletRequest#getReader
没有影响(但对HttpServletRequest#getParameter
有影响)?
- 是否有描述此类
ServletContext#setRequestCharacterEncoding
和 HttpServletRequest#getReader
行为的规范?
(我阅读了 Servlet Specification Version 4.0,但我找不到任何关于此类行为的规范。)
我创建了一个简单的 war 应用程序并测试了 ServletContext#setRequestCharacterEncoding
。
[环境]
- Tomcat9.0.19(我没有更改任何默认配置)
- JDK11
- Windows8.1
[index.html]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<form action="/SimpleWarApp/app/simple" method="post">
<!-- The value is Japanese character '\u3042' -->
<input type="text" name="hello" value="あ"/>
<input type="submit" value="submit!"/>
</form>
<button type="button" id="the_button">post</button>
<script>
document.getElementById('the_button').addEventListener('click', function() {
var xhttp = new XMLHttpRequest();
xhttp.open('POST', '/SimpleWarApp/app/simple');
xhttp.setRequestHeader('Content-Type', 'text/plain');
<!-- The body content is Japanese character '\u3042' -->
xhttp.send('あ');
});
</script>
</body>
</html>
[InitServletContextListener.java]
@WebListener
public class InitServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
sce.getServletContext().setRequestCharacterEncoding("UTF-8");
}
}
[SimpleServlet.java]
@WebServlet("/app/simple")
@SuppressWarnings("serial")
public class SimpleServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// req.setCharacterEncoding("UTF-8");
System.out.println("requestCharacterEncoding : " + req.getServletContext().getRequestCharacterEncoding());
System.out.println("req.getCharacterEncoding() : " + req.getCharacterEncoding());
String hello = req.getParameter("hello");
if (hello != null) {
System.out.println("hello : " + req.getParameter("hello"));
} else {
System.out.println("body : " + req.getReader().readLine());
}
}
}
我没有任何 servlet 过滤器。
以上三个就是这个war应用的全部组件。
(GitHub)
案例一:
当我提交带有参数'hello'的表单时,'hello'的值成功解码如下
requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
hello : あ
案例二:
当我点击'post'发送文本内容时,请求体无法成功解码如下。
(虽然我确认请求正文是这样用UTF-8编码的:E3 81 82
)
requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : ???
案例三:
当我还在 servlet 的 'doPost' 方法的第一行使用 HttpServletRequest#setCharacterEncoding
设置编码时,请求正文成功解码。
requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : あ
案例四:
当我使用 http.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8');
java 脚本时,请求正文已成功解码。
requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : あ
案例五:
当我不调用req.getParameter("hello")
时,无法成功解码请求体。
requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : ???
案例六:
当我不在 InitServletContextListener.java
调用 ServletContext#setRequestCharacterEncoding
时,没有设置字符编码。
requestCharacterEncoding : null
req.getCharacterEncoding() : null
body : ???
[注意]
(*)我这么认为是因为:
- (1)
HttpServletRequest#getReader
的 java 文档说
"The reader translates the character data according to the character encoding used on the body".
- (2)
HttpServletRequest#getCharacterEncoding
的 java 文档说
"Returns the name of the character encoding used in the body of this request".
- (3)
HttpServletRequest#getCharacterEncoding
的 java 文档也说
"The following methods for specifying the request character encoding are consulted, in decreasing order of priority: per request, per web app (using ServletContext.setRequestCharacterEncoding, deployment descriptor)".
ServletContext#setResponseCharacterEncoding
工作正常。当我使用ServletContext#setResponseCharacterEncoding
时,HttpServletResponse#getWriter
returns的writer按照自己设置的字符编码对response body进行编码
这是一个 Apache Tomcat 错误(特定于 getReader()
),将在 9.0.21 之后修复,感谢您在 Tomcat 用户邮件列表中的报告。
为了好奇,这里是 fix。
我们可以通过ServletContext#setRequestCharacterEncoding
(自 Servlet 4.0 起)设置用于读取请求主体的默认字符编码。
我认为 HttpServletRequest#getReader
的字符编码可以使用 ServletContext#setRequestCharacterEncoding(*)
设置。
但是 HttpServletRequest#getReader
returns 的 reader 似乎没有使用 ServletContext#setRequestCharacterEncoding
设置的编码解码字符。
我的问题是:
- 为什么
ServletContext#setRequestCharacterEncoding
对HttpServletRequest#getReader
没有影响(但对HttpServletRequest#getParameter
有影响)? - 是否有描述此类
ServletContext#setRequestCharacterEncoding
和HttpServletRequest#getReader
行为的规范?
(我阅读了 Servlet Specification Version 4.0,但我找不到任何关于此类行为的规范。)
我创建了一个简单的 war 应用程序并测试了 ServletContext#setRequestCharacterEncoding
。
[环境]
- Tomcat9.0.19(我没有更改任何默认配置)
- JDK11
- Windows8.1
[index.html]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<form action="/SimpleWarApp/app/simple" method="post">
<!-- The value is Japanese character '\u3042' -->
<input type="text" name="hello" value="あ"/>
<input type="submit" value="submit!"/>
</form>
<button type="button" id="the_button">post</button>
<script>
document.getElementById('the_button').addEventListener('click', function() {
var xhttp = new XMLHttpRequest();
xhttp.open('POST', '/SimpleWarApp/app/simple');
xhttp.setRequestHeader('Content-Type', 'text/plain');
<!-- The body content is Japanese character '\u3042' -->
xhttp.send('あ');
});
</script>
</body>
</html>
[InitServletContextListener.java]
@WebListener
public class InitServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
sce.getServletContext().setRequestCharacterEncoding("UTF-8");
}
}
[SimpleServlet.java]
@WebServlet("/app/simple")
@SuppressWarnings("serial")
public class SimpleServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// req.setCharacterEncoding("UTF-8");
System.out.println("requestCharacterEncoding : " + req.getServletContext().getRequestCharacterEncoding());
System.out.println("req.getCharacterEncoding() : " + req.getCharacterEncoding());
String hello = req.getParameter("hello");
if (hello != null) {
System.out.println("hello : " + req.getParameter("hello"));
} else {
System.out.println("body : " + req.getReader().readLine());
}
}
}
我没有任何 servlet 过滤器。 以上三个就是这个war应用的全部组件。 (GitHub)
案例一: 当我提交带有参数'hello'的表单时,'hello'的值成功解码如下
requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
hello : あ
案例二:
当我点击'post'发送文本内容时,请求体无法成功解码如下。
(虽然我确认请求正文是这样用UTF-8编码的:E3 81 82
)
requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : ???
案例三:
当我还在 servlet 的 'doPost' 方法的第一行使用 HttpServletRequest#setCharacterEncoding
设置编码时,请求正文成功解码。
requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : あ
案例四:
当我使用 http.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8');
java 脚本时,请求正文已成功解码。
requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : あ
案例五:
当我不调用req.getParameter("hello")
时,无法成功解码请求体。
requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : ???
案例六:
当我不在 InitServletContextListener.java
调用 ServletContext#setRequestCharacterEncoding
时,没有设置字符编码。
requestCharacterEncoding : null
req.getCharacterEncoding() : null
body : ???
[注意]
(*)我这么认为是因为:
- (1)
HttpServletRequest#getReader
的 java 文档说"The reader translates the character data according to the character encoding used on the body".
- (2)
HttpServletRequest#getCharacterEncoding
的 java 文档说"Returns the name of the character encoding used in the body of this request".
- (3)
HttpServletRequest#getCharacterEncoding
的 java 文档也说"The following methods for specifying the request character encoding are consulted, in decreasing order of priority: per request, per web app (using ServletContext.setRequestCharacterEncoding, deployment descriptor)".
- (1)
ServletContext#setResponseCharacterEncoding
工作正常。当我使用ServletContext#setResponseCharacterEncoding
时,HttpServletResponse#getWriter
returns的writer按照自己设置的字符编码对response body进行编码
这是一个 Apache Tomcat 错误(特定于 getReader()
),将在 9.0.21 之后修复,感谢您在 Tomcat 用户邮件列表中的报告。
为了好奇,这里是 fix。