【问题标题】:Why 'ServletContext#setRequestCharacterEncoding' does not have an effect on 'HttpServletRequest#getReader'?为什么“ServletContext#setRequestCharacterEncoding”对“HttpServletRequest#getReader”没有影响?
【发布时间】:2019-09-28 22:14:58
【问题描述】:

我们可以通过ServletContext#setRequestCharacterEncoding(从 Servlet 4.0 开始)设置用于读取请求正文的默认字符编码。

我认为HttpServletRequest#getReader的字符编码可以使用ServletContext#setRequestCharacterEncoding(*)来设置。

HttpServletRequest#getReader 返回的阅读器似乎解码的字符不使用ServletContext#setRequestCharacterEncoding 设置的编码。

我的问题是:

  • 为什么ServletContext#setRequestCharacterEncodingHttpServletRequest#getReader没有影响(但对HttpServletRequest#getParameter有影响)?
  • 是否有任何规范描述此类ServletContext#setRequestCharacterEncodingHttpServletRequest#getReader 行为?

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

我创建了一个简单的战争应用程序并测试了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)

案例 1: 当我提交带有参数'hello'的表单时,'hello'的值被成功解码如下。

requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
hello : あ

案例 2: 当我单击“发布”并发送文本内容时,请求正文无法成功解码,如下所示。 (虽然我确认请求正文是这样用 UTF-8 编码的:E3 81 82

requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : ???

案例 3: 当我还在 servlet 的“doPost”方法的第一行使用HttpServletRequest#setCharacterEncoding 设置编码时,请求正文成功解码。

requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : あ

案例 4: 当我使用http.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8'); javascript 时,请求正文成功解码。

requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : あ

案例 5: 当我不调用req.getParameter("hello")时,无法成功解码请求体。

requestCharacterEncoding : UTF-8
req.getCharacterEncoding() : UTF-8
body : ???

案例 6: 当我没有在InitServletContextListener.java 调用ServletContext#setRequestCharacterEncoding 时,没有设置字符编码。

requestCharacterEncoding : null
req.getCharacterEncoding() : null
body : ???

[注意]

  • (*)我这么认为是因为:

    • (1) HttpServletRequest#getReader 的 java 文档说

      “阅读器根据正文使用的字符编码翻译字符数据”。

    • (2) HttpServletRequest#getCharacterEncoding 的 java 文档说

      “返回此请求正文中使用的字符编码的名称”。

    • (3) HttpServletRequest#getCharacterEncoding 的 java doc 也说

      “以下方法用于指定请求字符编码,按优先级降序排列:每个请求,每个 Web 应用程序(使用 ServletContext.setRequestCharacterEncoding,部署描述符)”。

  • ServletContext#setResponseCharacterEncoding 工作正常。当我使用ServletContext#setResponseCharacterEncoding时,HttpServletResponse#getWriter返回的writer将响应体按照它设置的字符编码进行编码。

【问题讨论】:

  • 如果你使用 `http.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8'); 会发生什么` javascript?你的发现很有趣。如果在读取正文缓冲区之前不调用 `req.getParameter("hello")` 会发生什么?
  • 另外,您在 servlet 上是否有任何请求过滤器来弄乱 request.characterencoding 属性?如果您不设置 context.characterencoding 是否有区别。如果没有设置值,我认为你应该从 request.getCharacterEncoding() 中获取 NULL。
  • 我已经测试了`http.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8'); ` javascript(案例 4)和 servlet 未调用 req.getParameter("hello")(案例 5)。我已经编辑了我的问题。
  • 我没有任何 Servlet 过滤器。以上三个都是我的war应用的组件。我测试了没有调用ServletContext#setRequestCharacterEncoding(案例6)的应用程序。我已经编辑了我的问题。
  • 可能是 Tomcat 错误。我认为最好不要使用context.setRequestCharacterEncoding 方法。检查request.getCharacterEncoding()==null,然后在每个 servlet 代码上设置 UTF-8 编码。

标签: java servlet-4


【解决方案1】:

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

对于好奇的人,这里是fix

【讨论】:

    猜你喜欢
    • 2013-01-18
    • 2019-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-08
    • 2016-08-19
    • 2018-06-04
    • 1970-01-01
    相关资源
    最近更新 更多