Вход Unicode, полученный через входные компоненты PrimeFaces, поврежден

Когда я все еще использовал PrimeFaces v2.2.1, я смог ввести ввод в Юникод, такой как китайский, с входным компонентом PrimeFaces, таким как <p:inputText> и <p:editor>, и получить вход в хорошей форме в управляемом bean метод.

Однако, после того, как я обновился до PrimeFaces v3.1.1, все эти символы превратятся в Mojibake или вопросительные знаки. Только латинский ввод прекрасен, это символы китайского, арабского, иврита, кириллицы и т.д., Которые становятся искаженными.

Как это вызвано и как я могу его решить?

Ответ 1

Введение

Обычно JSF/Facelets будет устанавливать кодировку символов параметра запроса в UTF-8 по умолчанию уже при создании/восстановлении представления. Но если какой-либо параметр запроса был запрошен до того, как представление было создано/восстановлено, то слишком поздно установить правильную кодировку символов. Параметры запроса будут анализироваться только один раз.

Ошибка кодирования PrimeFaces

Это не удалось в PrimeFaces 3.x после обновления из 2.x вызвано новым переопределением isAjaxRequest() в PrimeFaces 'PrimePartialViewContext, который проверяет параметр запроса:

@Override
public boolean isAjaxRequest() {
    return getWrapped().isAjaxRequest()
            || FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().containsKey("javax.faces.partial.ajax");
}

По умолчанию isAjaxRequest() (один из Mojarra/MyFaces, как и предыдущий код PrimeFaces, полученный с помощью getWrapped()), проверяет заголовок запроса следующим образом, который не влияет на кодировку параметра запроса, поскольку параметры запроса не будут анализируется при получении заголовка запроса:

    if (ajaxRequest == null) {
        ajaxRequest = "partial/ajax".equals(ctx.
            getExternalContext().getRequestHeaderMap().get("Faces-Request"));
    }

Тем не менее, isAjaxRequest() может быть вызван любым слушателем фазы или прослушивателем системных событий или некоторым приложением factory до того, как представление было создано/восстановлено. Итак, когда вы используете PrimeFaces 3.x, параметры запроса будут проанализированы до того, как будет установлена ​​правильная кодировка символов, и, следовательно, используйте кодировку по умолчанию для сервера, которая обычно является ISO-8859-1. Это все испортит.

Решение

Есть несколько способов исправить это:

  • Используйте фильтр сервлета, который устанавливает ServletRequest#setCharacterEncoding() с UTF-8. Установка кодировки ответа ServletResponse#setCharacterEncoding(), кстати, не нужна, так как эта проблема не будет затронута этой проблемой.

    @WebFilter("/*")
    public class CharacterEncodingFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
            request.setCharacterEncoding("UTF-8");
            chain.doFilter(request, response);
        }
    
        // ...
    }
    

    Вам нужно только учитывать, что HttpServletRequest#setCharacterEncoding() устанавливает только параметры запроса POST-запроса, а не параметры запроса GET. Для параметров запроса GET вам все равно нужно настроить его на уровне сервера.

    Если вы используете библиотеку служебных программ JSF OmniFaces, такой фильтр уже предоставлен в поле, CharacterEncodingFilter. Просто установите его, как показано ниже, в web.xml в качестве первой записи фильтра:

    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.omnifaces.filter.CharacterEncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

  • Переконфигурируйте сервер для использования UTF-8 вместо ISO-8859-1 в качестве кодировки по умолчанию. В случае Glassfish это будет связано с добавлением следующей записи в <glassfish-web-app> файла /WEB-INF/glassfish-web.xml:

    <parameter-encoding default-charset="UTF-8" />
    

    Tomcat не поддерживает его. Он имеет атрибут URIEncoding в записи <Context>, но это относится только к запросам GET, а не к запросам POST.


  • Сообщить об этом как об ошибке для PrimeFaces. Есть ли какая-либо законная причина, чтобы проверить, что HTTP-запрос является ajax-запросом, проверяя параметр запроса вместо заголовка запроса, как это было бы для стандартного JSF и, например, jQuery? Этот скрипт PrimeFaces 'core.js делает это. Было бы лучше, если бы он установил его как заголовок запроса XMLHttpRequest.


Решения, которые НЕ работают

Возможно, вы столкнетесь с ниже "решениями" где-то в Интернете, исследуя эту проблему. Эти решения никогда не будут работать в этом конкретном случае. Пояснение следует.

  • Настройка пролога:

    <?xml version='1.0' encoding='UTF-8' ?>
    

    Это только говорит парсеру XML использовать UTF-8 для декодирования источника XML, прежде чем создавать дерево XML вокруг него. Парсер XML, фактически используемый Facelts, SAX во время JSF показывает время сборки. Эта часть не имеет ничего общего с кодировкой HTTP-запроса/ответа.

  • Настройка метатега HTML:

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    

    Метатег HTML игнорируется, когда страница передается через HTTP через URI http(s)://. Он использовался только тогда, когда страница была сохранена клиентом как файл HTML на локальной дисковой системе, а затем повторно открыта с помощью URI file:// в браузере.

  • Настройка HTML-формы принимает атрибут charset:

    <h:form accept-charset="UTF-8">
    

    Современные браузеры игнорируют это. Это имеет эффект только в браузере Microsoft Internet Explorer. Даже тогда это делает это неправильно. Никогда не используйте его. Все настоящие веб-браузеры будут использовать атрибут charset, указанный в заголовке Content-Type ответа. Даже MSIE сделает это правильно, пока вы не укажете атрибут accept-charset.

  • Настройка аргумента JVM:

    -Dfile.encoding=UTF-8
    

    Это используется только JVM Oracle (!) для чтения и анализа исходных файлов Java.