为什么Tomcat没有响应编码设置?我怎么处理它?

时间:2010-03-24 16:49:07

标签: java tomcat encoding servlets

我最近遇到了servlet生成的网站编码问题,如果servlet是在Tomcat下部署的,而不是在Jetty下部署的。我对它进行了一些研究并将问题简化为以下servlet:

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();
    }
}

如果我在Jetty下部署它并将浏览器指向它,它会返回预期的结果。数据以ISO-8859-1的形式返回,如果我查看标题,那么Jetty会返回:

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

浏览器会检测此标头的编码。如果我在Tomcat中部署相同的servlet,浏览器会显示奇怪的字符。但是Tomcat也将数据作为ISO-8859-1返回,不同之处在于没有标题告诉它。所以浏览器必须猜测编码,这就出错了。

我的问题是,Tomcat的行为是正确还是错误?如果它是正确的,我该如何避免这个问题?当然,我总是可以将response.setCharacterEncoding("UTF-8");添加到servlet,但这意味着我设置了一个固定的编码,浏览器可能会或可能不会理解。如果没有浏览器而另一个服务访问servlet,则问题更为相关。那么我应该如何以最灵活的方式处理这个问题呢?

4 个答案:

答案 0 :(得分:4)

如果未指定编码,则Servlet规范要求ISO-8859-1。但是,AFAIK它不要求容器在内容类型中设置编码,至少在将其设置为“text / plain”时不需要。这就是规范所说的:

  

调用setContentType设置   只有给定的字符编码   内容类型字符串提供值   对于charset属性。

换句话说,只有你设置了这样的内容类型

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

Tomcat需要设置charset。我还没有尝试过这是否有效。

一般情况下,我建议始终将编码设置为UTF-8(因为它会导致最少的麻烦,至少在浏览器中),然后,对于text / plain,明确说明编码,以防止浏览器使用系统默认值。

答案 1 :(得分:2)

为了支持Jesse Barnum的回答,apache Wiki建议使用过滤器来控制请求和响应的字符编码。但是,Tomcat 5.5及更高版本捆绑了一个SetCharacterEncodingFilter,所以使用apache的实现可能比使用Jesse更好(没有攻击Jesse)。 tomcat实现只在请求上设置字符编码,因此可能需要修改以使用过滤器作为在所有servlet的响应上设置字符集的方法。

具体来说,Tomcat在这里有实现示例:

  

5.x的

     

的webapps / servlet的-实例/ WEB-INF /类/滤波器/ SetCharacterEncodingFilter.java

     

的webapps / JSP-实例/ WEB-INF /类/滤波器/ SetCharacterEncodingFilter.java

     

6.x的

     

web应用/实施例/ WEB-INF /类/滤波器/ SetCharacterEncodingFilter.java

     

7.x的

     

自7.0.20以来,过滤器成为一等公民,并从示例转移到核心Tomcat,可用于任何Web应用程序,而无需单独编译和捆绑。有关Tomcat提供的过滤器列表,请参阅文档。班级名称是:   org.apache.catalina.filters.SetCharacterEncodingFilter

此页面详细说明:http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q3

答案 2 :(得分:0)

这是我写的强制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() {}

}

答案 3 :(得分:-1)

如果您没有指定编码,Tomcat可以自由编码您的字符但感觉,并且浏览器可以自由猜测Tomcat选择的编码。你是正确的,解决问题的方法是response.setCharacterEncoding("UTF-8")

您不必担心浏览器无法理解编码的可能性,因为在过去10年中发布的几乎所有浏览器都支持UTF-8。虽然您真的很担心,但您可以检查用户代理提供的“Accept-Encoding”标头。