Entrada Unicode recuperada via componentes de input PrimeFaces corrompidos

Quando eu ainda estava usando o PrimeFaces v2.2.1, eu era capaz de digitar uma input unicode como o chinês com um componente de input PrimeFaces como

e

, e recuperar a input em boa forma no método do bean gerenciado .

No entanto, depois de fazer o upgrade para o PrimeFaces v3.1.1, todos esses caracteres se tornam Mojibake ou pontos de interrogação. Apenas a input em latim vem bem, são os caracteres chinês, árabe, hebraico, cirílico, etc que ficam malformados.

Como isso é causado e como posso resolvê-lo?

Introdução

Normalmente, o JSF / Facelets irá definir a codificação de caracteres do parâmetro de solicitação para UTF-8 por padrão quando a visão é criada / restaurada. Mas, se qualquer parâmetro de solicitação for solicitado antes que a visualização seja criada / restaurada, é tarde demais para definir a codificação correta de caracteres. Os parâmetros da solicitação serão analisados ​​apenas uma vez.

Codificação PrimeFaces falha

O fato de ter falhado em PrimeFaces 3.x após a atualização de 2.x é causado pela nova substituição de isAjaxRequest() no PrimePartialViewContext de PrimePartialViewContext que verifica um parâmetro de solicitação:

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

Por padrão, o isAjaxRequest() (o de Mojarra / MyFaces, como o código PrimeFaces acima obteve por getWrapped() ) verifica o header da solicitação como segue, o que não afeta a codificação do parâmetro request, pois os parâmetros de solicitação não serão analisados ​​quando um header de solicitação é obtido:

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

No entanto, o isAjaxRequest() pode ser chamado por qualquer ouvinte de fase ou ouvinte de evento do sistema ou algum aplicativo de fábrica antes que a exibição seja criada / restaurada. Então, quando você estiver usando o PrimeFaces 3.x, os parâmetros da solicitação serão analisados antes que a codificação de caracteres apropriada seja definida e, portanto, use a codificação padrão do servidor, que geralmente é ISO-8859-1. Isso vai estragar tudo.

Soluções

Existem várias maneiras de corrigir isso:

  1. Use um filtro de servlet que define ServletRequest#setCharacterEncoding() com UTF-8. Definir a codificação de resposta por ServletResponse#setCharacterEncoding() é, a propósito, desnecessário, pois não será afetado por esse problema.

     @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); } // ... } 

    Você só precisa levar em conta que HttpServletRequest#setCharacterEncoding() só define a codificação para parâmetros de solicitação POST, não para parâmetros de solicitação GET. Para parâmetros de solicitação GET, você ainda precisa configurá-lo no nível do servidor.

    Se acontecer de você usar a biblioteca de utilitários JSF OmniFaces , esse filtro já estará disponível na checkbox, o CharacterEncodingFilter . Basta instalá-lo como abaixo em web.xml como a primeira input de filtro:

      characterEncodingFilter org.omnifaces.filter.CharacterEncodingFilter   characterEncodingFilter /*  

  2. Reconfigure o servidor para usar o UTF-8 em vez do ISO-8859-1 como codificação padrão. No caso do Glassfish, seria uma questão de adicionar a seguinte input ao do arquivo /WEB-INF/glassfish-web.xml :

      

    O Tomcat não suporta isso. Ele tem o atributo URIEncoding na input , mas isso se aplica apenas a solicitações GET, não a solicitações POST.


  3. Relatar como um erro para PrimeFaces. Existe realmente algum motivo legítimo para verificar a solicitação HTTP sendo uma solicitação de ajax, verificando um parâmetro de solicitação em vez de um header de solicitação, como faria com o JSF padrão e, por exemplo, com o jQuery? O JavaScript do core.js PrimeFaces está fazendo isso. Seria melhor se ele fosse definido como um header de solicitação de XMLHttpRequest .


Soluções que NÃO funcionam

Talvez você encontre abaixo “soluções” em algum lugar da Internet enquanto investiga esse problema. Essas soluções nunca funcionarão neste caso específico. Explicação segue.

  • Configurando o prólogo XML:

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

    Isso apenas instrui o analisador XML a usar o UTF-8 para decodificar a fonte XML antes de construir a tree XML em torno dela. O analisador XML que está sendo usado pelo Facelts é o SAX durante o tempo de construção da visualização JSF. Esta parte não tem nada a ver com codificação de solicitação / resposta HTTP.

  • Definindo a meta tag HTML:

      

    A meta tag HTML é ignorada quando a página é veiculada via HTTP por meio de um http(s):// URI. Ele só é usado quando a página é salva pelo cliente como um arquivo HTML no sistema de disco local e depois reaberto por um file:// URI no navegador.

  • A configuração do formulário HTML aceita o atributo charset:

      

    Navegadores modernos ignoram isso. Isso só tem efeito no navegador Microsoft Internet Explorer. Mesmo assim, está fazendo errado. Nunca use isso. Todos os webbrowsers reais usarão o atributo charset especificado no header Content-Type da resposta. Mesmo o MSIE fará isso da maneira correta, desde que você não especifique o atributo accept-charset .

  • Definindo o argumento da JVM:

     -Dfile.encoding=UTF-8 

    Isso é usado apenas pela JVM do Oracle (!) Para ler e analisar os arquivos de origem Java.