15

<p:inputText>当我还在使用 PrimeFaces v2.2.1 时,我能够使用 PrimeFaces 输入组件(例如和)输入 unicode 输入(例如中文)<p:editor>,并在托管 bean 方法中以良好的形式检索输入。

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

这是如何引起的,我该如何解决?

4

1 回答 1

27

介绍

通常,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");
}

默认情况下,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. 使用设置为 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);
        }
    
        // ...
    }
    

    您只需要考虑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>
    

  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.jsJavaScript 就是这样做的。如果将其设置为XMLHttpRequest.


不起作用的解决方案

也许您在调查此问题时会在 Internet 上的某个地方偶然发现以下“解决方案”。这些解决方案在这种特定情况下永远不会起作用。解释如下。

  • 设置 XML 序言:

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

    这只是告诉 XML 解析器在围绕它构建 XML 树之前使用 UTF-8 对 XML 源进行解码。在 JSF视图构建期间,Facelts 实际使用的 XML 解析器是 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 浏览器中有效。即使那样,它也做错了。永远不要使用它。所有真正的网络浏览器都将使用Content-Type响应标头中指定的字符集属性。只要您不指定accept-charset属性,即使 MSIE 也会以正确的方式执行此操作。

  • 设置 JVM 参数:

    -Dfile.encoding=UTF-8
    

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

于 2012-03-23T12:40:16.743 回答