2010-03-24 31 views
5

Recientemente tuve un problema con la codificación de sitios web generados por servlet, que ocurría si los servlets se implementaban en Tomcat, pero no en Jetty. Hice un poco de investigación sobre el tema y simplificado el problema a la siguiente servlet:¿Por qué Tomcat no ha configurado ninguna codificación? ¿Cómo puedo lidiar con eso?

public class TestServlet extends HttpServlet implements Servlet { 
    @Override 
    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException { 
     response.setContentType("text/plain"); 
     Writer output = response.getWriter(); 
     output.write("öäüÖÄÜß"); 
     output.flush(); 
     output.close(); 
    } 
} 

Si puedo implementar esto bajo el embarcadero y dirigir el navegador a ella, se devuelve el resultado esperado. Los datos se devuelven como ISO-8859-1 y si echo un vistazo a las cabeceras, a continuación, vuelve embarcadero:

Content-Type: text/plain; charset=iso-8859-1 

El navegador detecta la codificación de esta cabecera. Si despliego el mismo servlet en Tomcat, el navegador muestra caracteres extraños. Pero Tomcat también devuelve los datos como ISO-8859-1, la diferencia es que ningún encabezado lo menciona. Entonces el navegador tiene que adivinar la codificación, y eso va mal.

Mi pregunta es, ¿es correcto el comportamiento de Tomcat o un error? Y si es correcto, ¿cómo puedo evitar este problema? Claro, siempre puedo agregar response.setCharacterEncoding("UTF-8"); al servlet, pero eso significa que establezco una codificación fija, que el navegador puede o no comprender. El problema es más relevante, si ningún navegador sino otro servicio accede al servlet. Entonces, ¿cómo debo tratar el problema de la manera más flexible?

+0

BTW: 'implementa Servlet' es superflua, ya que' HttpServlet' ya se hace eso. – BalusC

Respuesta

-1

Si no especifica la codificación, Tomcat es libre de codificar sus caracteres como quiera, y el navegador es libre de adivinar qué codificación eligió Tomcat. Tiene razón en que la forma de resolver el problema es response.setCharacterEncoding("UTF-8").

No debe preocuparse por la posibilidad de que el navegador no comprenda la codificación, ya que prácticamente todos los navegadores lanzados en los últimos 10 años son compatibles con UTF-8. Aunque si realmente está preocupado, puede inspeccionar los encabezados "Aceptar codificación" proporcionados por el agente de usuario.

+1

Eso no es correcto, la especificación requiere ISO-8859-1 como codificación predeterminada. –

+0

No tengo ningún problema con que tomcat escoja una codificación, pero hay un problema con el hecho de que tomcat no le dice al navegador qué codificación estaba eligiendo. Y, como escribí, los navegadores modernos pueden admitir codificaciones ISO y Unicode, pero otros programas pueden acceder a servicios proporcionados por servlets. – Dishayloo

+0

@Tim: ¿Qué especificación sería esa? Diría que probablemente sea irrelevante en este caso. –

4

Si no especifica una codificación, la especificación del servlet requiere ISO-8859-1. Sin embargo, AFAIK no requiere que el contenedor establezca la codificación en el tipo de contenido, al menos no si lo configura en "texto/normal". Esto es lo que dice la especificación:

llamadas a setContentType establecer la codificación de caracteres sólo si el tipo de contenido cadena dada proporciona un valor para el atributo juego de caracteres.

En otras palabras, sólo si se establece el tipo de contenido como esto

response.setContentType("text/plain; charset=XXXX") 

Tomcat se requiere para configurar el juego de caracteres. No he intentado si esto funciona bien.

En general, recomendaría configurar siempre la codificación en UTF-8 (ya que causa la menor cantidad de problemas, al menos en los navegadores) y luego, para texto/simple, indicar la codificación explícitamente, para evitar navegadores de usar un sistema predeterminado.

+0

Hmm, ¿el comportamiento de Jetty es incorrecto? Jetty hace las cosas mucho más fáciles en este caso, ya que funciona como se esperaba. – Dishayloo

+0

Creo que sí. O al menos no puedo encontrar nada en la especificación que diga que Jetty debería modificar el tipo de contenido en este caso. –

0

Aquí hay un filtro que escribí para forzar codificación UTF-8:

public class CharacterEncodingFilter implements Filter { 
private static final Logger log = Logger.getLogger(CharacterEncodingFilter.class.getName()); 

boolean isConnectorConfigured = false; 

public void init(FilterConfig filterConfig) throws ServletException {} 

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
    request.setCharacterEncoding("utf-8"); 
    response.setCharacterEncoding("utf-8"); 
    if(! isConnectorConfigured) { 
     isConnectorConfigured = true; 
     try { //I need to do all of this with reflection, because I get NoClassDefErrors otherwise. --jsb 
      Field f = request.getClass().getDeclaredField("request"); //Tomcat wraps the real request in a facade, need to get it 
      f.setAccessible(true); 
      Object req = f.get(request); 
      Object connector = req.getClass().getMethod("getConnector", new Class[0]).invoke(req); //Now get the connector 
      connector.getClass().getMethod("setUseBodyEncodingForURI", new Class[] {boolean.class}).invoke(connector, Boolean.TRUE); 
     } catch(NoSuchFieldException e) { 
      log.log(Level.WARNING, "Servlet container does not seem to be Tomcat, cannot programatically alter character encoding. Do this in the Server.xml <Connector> attribute instead."); 
     } catch(Exception e) { 
      log.log(Level.WARNING, "Could not setUseBodyEncodingForURI to true on connector"); 
     } 
    } 
    chain.doFilter(request, response); 
} 

public void destroy() {} 

}

2

En apoyo de la respuesta de Jesse Barnum, el Apache Wiki sugiere que un filtro se puede utilizar para controlar el carácter codificación de la solicitud y la respuesta. Sin embargo, Tomcat 5.5 y superior vienen incluidos con SetCharacterEncodingFilter por lo que puede ser mejor utilizar la implementación de apache que usar Jesse (sin ofender a Jesse). Las implementaciones de tomcat solo establecen la codificación de caracteres en la solicitud, por lo que puede ser necesaria una modificación para usar el filtro como un medio para establecer el juego de caracteres en la respuesta de todos los servlets.

Específicamente, Tomcat tiene implementaciones ejemplos aquí:

5.x

webapps/servlets-examples/WEB-INF/classes/filtros/SetCharacterEncodingFilter.java

webapps/jsp- ejemplos/WEB-INF/classes/filtros/SetCharacterEncodingFilter.java

6.x

webapps/examples/WEB-INF/classes/filtros/SetCharacterEncodingFilter.java

7.x

Desde 7.0.20 el filtro se convirtió de primera clase de los ciudadanos y se trasladó de los ejemplos en el núcleo Tomcat y está disponible a cualquier aplicación web sin la necesidad de compilarla y agruparla por separado. Consulte la documentación de la lista de filtros proporcionada por Tomcat. El nombre de la clase es: org.apache.catalina.filters.SetCharacterEncodingFilter

Esta página dice más: http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q3

Cuestiones relacionadas