通过PrimeFaces输入组件检索的Unicode输入已损坏

Mr.*_*mes 13 unicode jsf character-encoding primefaces mojibake

当我还是用PrimeFaces V2.2.1,我能键入Unicode输入诸如与PrimeFaces输入组件中国如<p:inputText><p:editor>,并检索在管理bean方法好形状的输入.

但是,在我升级到PrimeFaces v3.1.1后,所有这些字符都变成了Mojibake或问号.只有拉丁语输入才能正常,中文,阿拉伯语,希伯来语,西里尔语等字符会变得格格不入.

这是怎么造成的,我该如何解决?

Bal*_*usC 24

介绍

通常,在创建/恢复视图时,JSF/Facelets会将请求参数字符编码默认设置为UTF-8.但是,如果在创建/恢复视图之前请求了任何请求参数,那么设置正确的字符编码为时已晚.请求参数将仅被解析一次.

PrimeFaces编码失败

从2.x升级后它在PrimeFaces 3.x中失败是由isAjaxRequest()PrimeFaces中的新覆盖PrimePartialViewContext检查请求参数引起的:

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

默认情况下isAjaxRequest()(Mojarra/MyFaces之一,如上面的PrimeFaces代码所获得的那样getWrapped())检查请求头如下,这不会影响请求参数编码,因为获取请求头时将不会解析请求参数:

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

但是,在创建/恢复视图之前,isAjaxRequest()可以由任何阶段侦听器或系统事件侦听器或某些应用程序工厂调用.因此,当您使用PrimeFaces 3.x时,将设置正确的字符编码之前解析请求参数,因此使用服务器的默认编码,通常是ISO-8859-1.这会弄乱一切.

解决方案

有几种方法可以解决它:

  1. 使用使用UTF-8 设置的servlet过滤器ServletRequest#setCharacterEncoding().设置响应编码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);
        }
    
        // ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

    您只需要考虑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>
    
    Run Code Online (Sandbox Code Playgroud)
  2. 重新配置服务器以使用UTF-8而不是ISO-8859-1作为默认编码.GlassFish中的情况下,这将是添加以下条目的事<glassfish-web-app>了的/WEB-INF/glassfish-web.xml文件:

    <parameter-encoding default-charset="UTF-8" />
    
    Run Code Online (Sandbox Code Playgroud)

    Tomcat不支持它.它URIEncoding<Context>条目中具有属性,但这仅适用于GET请求,而不适用于POST请求.


  3. 将其报告为PrimeFaces的错误.有没有真正的任何正当理由,以检查HTTP请求是通过检查的请求参数,而不是请求头一样,你会为标准JSF和例如jQuery的做一个Ajax请求?PrimeFaces的core.jsJavaScript正在这样做.如果它将其设置为请求标头会更好XMLHttpRequest.


解决方案不起作用

也许你会在调查这个问题时偶然发现互联网上某处的"解决方案".这些解决方案不会在这种特定情况下起作用.说明如下.

  • 设置XML prolog:

    <?xml version='1.0' encoding='UTF-8' ?>
    
    Run Code Online (Sandbox Code Playgroud)

    这只会告诉XML解析器在构建XML源代码之前使用UTF-8解码XML源代码.在JSF 视图构建期间,Facelts实际使用的XML解析器是SAX .这部分与HTTP请求/响应编码完全无关.

  • 设置HTML元标记:

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    
    Run Code Online (Sandbox Code Playgroud)

    当通过http(s)://URI 通过HTTP提供页面时,将忽略HTML元标记.仅当页面由客户端保存为本地磁盘系统上的HTML文件,然后由file://浏览器中的URI 重新打开时,才会使用它.

  • 设置HTML表单接受charset属性:

    <h:form accept-charset="UTF-8">
    
    Run Code Online (Sandbox Code Playgroud)

    现代浏览器忽略这一点 这仅在Microsoft Internet Explorer浏览器中有效.即便如此,它也是错误地做错了.永远不要使用它.所有真正的Web浏览器都将使用Content-Type响应头中指定的charset属性.只要您没有指定accept-charset属性,即使MSIE也会以正确的方式执行.

  • 设置JVM参数:

    -Dfile.encoding=UTF-8
    
    Run Code Online (Sandbox Code Playgroud)

    这仅供Oracle(!)JVM用于读取和解析Java源文件.