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

时间:2012-03-09 12:42:50

标签: jsf unicode primefaces character-encoding mojibake

当我还在使用PrimeFaces v2.2.1时,我能够使用诸如<p:inputText><p:editor>之类的PrimeFaces输入组件键入unicode输入,例如中文,并检索输入状态良好的输入托管bean方法。

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

这是如何引起的?如何解决?

1 个答案:

答案 0 :(得分:22)

简介

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

PrimeFaces编码失败

从2.x升级后,PrimeFaces 3.x失败的原因是PrimeFaces isAjaxRequest()中的新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()。因此,当您使用PrimeFaces 3.x时,将在设置正确的字符编码之前解析请求参数,因此使用服务器的默认编码,通常是ISO-8859-1。这会弄乱一切。

解决方案

有几种方法可以解决它:

  1. 使用servlet filter设置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,则已在框the 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>
    

  2. 重新配置服务器以使用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请求。


  3. 将其报告为PrimeFaces的错误。是否确实通过检查请求参数而不是像标准JSF和例如jQuery那样检查请求标头来检查HTTP请求是否为ajax请求的任何合法理由? PrimeFaces的core.js JavaScript正在这样做。如果它将其设置为XMLHttpRequest的请求标头会更好。


  4. 不起作用的解决方案

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

    • 设置XML序言:

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

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

    • 设置HTML元标记:

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

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

    • 设置HTML表单接受字符集属性:

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

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

    • 设置JVM参数:

      -Dfile.encoding=UTF-8
      

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