Почему Tomcat не устанавливает кодировку в ответ? Как я могу справиться с этим? - PullRequest
5 голосов
/ 24 марта 2010

У меня недавно была проблема с кодировкой сайтов, созданных сервлетом, которая возникала, если сервлеты были развернуты под Tomcat, но не под Jetty. Я провел небольшое исследование по этому поводу и упростил задачу до следующего сервлета:

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, в браузере появятся странные символы. Но Tomcat также возвращает данные в виде ISO-8859-1, разница в том, что ни один заголовок не говорит об этом. Поэтому браузер должен угадать кодировку, и это не так.

Мой вопрос такой: правильное ли поведение Tomcat или ошибка? И если это правильно, как я могу избежать этой проблемы? Конечно, я всегда могу добавить response.setCharacterEncoding("UTF-8"); к сервлету, но это означает, что я установил фиксированную кодировку, которую браузер может понимать или не понимать. Проблема более актуальна, если ни один браузер, кроме другой службы, не обращается к сервлету. Так как же мне наиболее гибко решить проблему?

Ответы [ 4 ]

4 голосов
/ 24 марта 2010

Если вы не укажете кодировку, спецификация сервлета требует ISO-8859-1. Однако AFAIK не требует, чтобы контейнер устанавливал кодировку в типе контента, по крайней мере, если вы установили его в «text / plain». Вот что говорится в спецификации:

Вызовы setContentType установить кодировка символов только если дано строка типа содержимого предоставляет значение для атрибута charset.

Другими словами, только если вы установите тип контента следующим образом

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

Tomcat требуется для установки кодировки. Я не пробовал, работает ли это все же.

В целом, я бы рекомендовал всегда устанавливать кодировку UTF-8 (так как это вызывает наименьшее количество проблем, по крайней мере, в браузерах), а затем, для text / plain, явно указывать кодировку, чтобы браузеры не используя системную настройку по умолчанию.

2 голосов
/ 20 февраля 2012

В поддержку ответа Джесси Барнума, вики apache предлагает использовать фильтр для управления кодировкой символов запроса и ответа. Тем не менее, Tomcat 5.5 и выше поставляются в комплекте с SetCharacterEncodingFilter, поэтому может быть лучше использовать реализацию apache, чем использовать Джесси (без обид Джесси). Реализации tomcat устанавливают только кодировку символов по запросу, поэтому может потребоваться модификация для использования фильтра в качестве средства установки набора символов в ответе всех сервлетов.

В частности, Tomcat имеет примеры реализации здесь:

5.x

WebApps / сервлеты-примеры / WEB-INF / классы / фильтры / SetCharacterEncodingFilter.java

WebApps / JSP-примеры / WEB-INF / классы / фильтры / SetCharacterEncodingFilter.java

6.x

WebApps / примеры / WEB-INF / классы / фильтры / SetCharacterEncodingFilter.java

7.x

Начиная с 7.0.20 фильтр стал первоклассным гражданином и был перенесен из примеров в ядро ​​Tomcat и доступен для любого веб-приложения без необходимости компилировать и связывать его отдельно. См. Документацию для списка фильтров, предоставляемых Tomcat. Имя класса: org.apache.catalina.filters.SetCharacterEncodingFilter

Эта страница рассказывает больше: http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q3

0 голосов
/ 15 февраля 2011

Вот фильтр, который я написал для принудительного кодирования 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() {}

}

0 голосов
/ 24 марта 2010

Если вы не укажете кодировку, Tomcat может свободно кодировать ваши символы, как ему кажется, и браузер может угадать, какую кодировку выбрал Tomcat. Вы правы в том, что способ решения проблемы - response.setCharacterEncoding("UTF-8").

Вам не нужно беспокоиться о вероятности того, что браузер не поймет кодировку, поскольку практически все браузеры, выпущенные за последние 10 лет, поддерживают UTF-8. Хотя, если вы действительно обеспокоены, вы можете проверить заголовки «Accept-Encoding», предоставленные пользовательским агентом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...