Почему "ServletContext#setRequestCharacterEncoding" не влияет на "HttpServletRequest#getReader"?

Мы можем установить кодировку символов по умолчанию, используемую для чтения тел запросов: ServletContext#setRequestCharacterEncoding (начиная с Servlet 4.0).

Я думаю, что кодировка символов для HttpServletRequest#getReader можно установить с помощью ServletContext#setRequestCharacterEncoding(*),

Но читатель, который HttpServletRequest#getReader возвращается, кажется, декодировать символы, не используя кодировку, установленную ServletContext#setRequestCharacterEncoding,

Мои вопросы:

  • Зачем ServletContext#setRequestCharacterEncoding не влияет на HttpServletRequest#getReader(но это влияет на HttpServletRequest#getParameter)?
  • Есть ли спецификация, описывающая такие ServletContext#setRequestCharacterEncoding а также HttpServletRequest#getReader поведения?

(Я прочитал спецификацию сервлетов версии 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());
        }
    }
}

У меня нет никаких сервлет-фильтров. Выше три все компоненты этого военного приложения. ( 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: Когда я также установил кодировку, используя HttpServletRequest#setCharacterEncoding вместо этого в первой строке метода doPost сервлета тело запроса успешно декодируется.

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: Когда я не звоню ServletContext#setRequestCharacterEncoding в InitServletContextListener.java, кодировка символов не установлена.

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

[НОТА]

  • (*) Я так думаю, потому что:

    • (1) Java документ HttpServletRequest#getReader говорит

      "Читатель переводит символьные данные в соответствии с кодировкой символов, используемой на теле".

    • (2) Java документ HttpServletRequest#getCharacterEncoding говорит

      Msgstr "Возвращает имя кодировки символов, используемой в теле этого запроса".

    • (3) Java документ HttpServletRequest#getCharacterEncoding также говорит

      "Следующие методы для указания кодировки символов запроса используются в порядке убывания приоритета: для каждого запроса, для каждого веб-приложения (с использованием ServletContext.setRequestCharacterEncoding, дескриптор развертывания)".

  • ServletContext#setResponseCharacterEncoding работает отлично. Когда я использую ServletContext#setResponseCharacterEncodingАвтор, который HttpServletResponse#getWriter Return кодирует тело ответа с помощью установленной им кодировки символов.

1 ответ

Решение

Это ошибка Apache Tomcat (относится к getReader()) это будет исправлено в 9.0.21 и далее благодаря вашему отчету в списке рассылки пользователей Tomcat.

Для любопытных вот исправление.

Другие вопросы по тегам