为什么 '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 设置的编码解码字符。

我的问题是:

(我阅读了 Servlet Specification Version 4.0,但我找不到任何关于此类行为的规范。)

我创建了一个简单的 war 应用程序并测试了 ServletContext#setRequestCharacterEncoding

[环境]

[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 : ???

[注意]

这是一个 Apache Tomcat 错误(特定于 getReader()),将在 9.0.21 之后修复,感谢您在 Tomcat 用户邮件列表中的报告。

为了好奇,这里是 fix