2010-03-24 20 views
5

Gần đây tôi đã gặp sự cố với việc mã hóa các trang web được tạo bởi servlet, xảy ra nếu các servlet được triển khai dưới Tomcat, nhưng không phải dưới Cầu cảng. Tôi đã thực hiện một ít nghiên cứu về nó và đơn giản hóa vấn đề với servlet sau:Tại sao bộ mã hóa không được Tomcat trả lời? Làm thế nào tôi có thể đối phó với nó?

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

Nếu tôi triển khai điều này dưới Cầu tàu và hướng trình duyệt đến nó, nó sẽ trả về kết quả mong đợi. Dữ liệu được trả lại dưới dạng ISO-8859-1 và nếu tôi xem xét các tiêu đề, thì Jetty sẽ trả về:

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

Trình duyệt phát hiện mã hóa từ tiêu đề này. Nếu tôi triển khai cùng một servlet trong Tomcat, trình duyệt sẽ hiển thị các ký tự lạ. Nhưng Tomcat cũng trả về dữ liệu như ISO-8859-1, sự khác biệt là, không có tiêu đề nào nói về nó. Vì vậy, trình duyệt phải đoán mã hóa và điều đó sai.

Câu hỏi của tôi là hành vi của Tomcat đúng hay lỗi? Và nếu nó là chính xác, làm thế nào tôi có thể tránh được vấn đề này? Chắc chắn, tôi luôn có thể thêm response.setCharacterEncoding("UTF-8"); vào servlet, nhưng điều đó có nghĩa là tôi đã đặt mã hóa cố định, trình duyệt có thể hoặc không hiểu. Vấn đề là phù hợp hơn, nếu không có trình duyệt nhưng một dịch vụ khác truy cập servlet. Vì vậy, làm thế nào tôi nên đối phó với vấn đề một cách linh hoạt nhất?

+0

Btw: 'thực hiện Servlet' là không cần thiết là' HttpServlet' đã thực hiện điều đó. – BalusC

Trả lời

-1

Nếu bạn không chỉ định mã hóa, Tomcat sẽ tự do mã hóa các ký tự của bạn, tuy nhiên, trình duyệt sẽ tự do đoán mã hóa mà Tomcat chọn. Bạn đúng trong cách giải quyết vấn đề là response.setCharacterEncoding("UTF-8").

Bạn không nên lo lắng về khả năng trình duyệt sẽ không hiểu mã hóa, vì hầu như tất cả các trình duyệt được phát hành trong 10 năm qua đều hỗ trợ UTF-8. Mặc dù nếu bạn thực sự lo lắng, bạn có thể kiểm tra các tiêu đề "Accept-Encoding" được cung cấp bởi tác nhân người dùng.

+1

Không chính xác, đặc điểm kỹ thuật yêu cầu ISO-8859-1 làm mã hóa mặc định. –

+0

Tôi không có vấn đề với tomcat chọn một mã hóa, nhưng một vấn đề với thực tế, mà tomcat không nói cho trình duyệt mà mã hóa nó đã được lựa chọn. Và như tôi đã viết, các trình duyệt hiện đại có thể hỗ trợ mã hóa ISO và Unicode, nhưng các chương trình khác có thể truy cập các dịch vụ do servlet cung cấp. – Dishayloo

+0

@Tim: Thông số nào sẽ là? Tôi muốn nói nó có lẽ không liên quan trong trường hợp này. –

4

Nếu bạn không chỉ định mã hóa, đặc tả Servlet yêu cầu ISO-8859-1. Tuy nhiên, AFAIK nó không yêu cầu vùng chứa để đặt mã hóa theo kiểu nội dung, ít nhất là nếu bạn đặt nó thành "văn bản/đồng bằng". Đây là những gì spec nói:

Các cuộc gọi đến setContentType thiết lập mã hóa nhân vật duy nhất nếu được kiểu nội dung chuỗi cung cấp một giá trị cho thuộc tính charset.

Nói cách khác, chỉ khi bạn thiết lập các kiểu nội dung như thế này

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

Tomcat là cần thiết để thiết lập charset. Tôi đã không cố gắng cho dù điều này hoạt động mặc dù.

Nói chung, tôi khuyên bạn nên luôn đặt mã hóa thành UTF-8 (vì nó gây ra ít rắc rối nhất, ít nhất là trong trình duyệt) và sau đó, đối với văn bản/đơn giản, hãy mã hóa rõ ràng để ngăn trình duyệt từ việc sử dụng hệ thống mặc định.

+0

Hmm, hành vi của Jetty có chính xác không? Cầu cảng làm cho mọi thứ dễ dàng hơn nhiều trong trường hợp này, vì nó hoạt động như mong đợi. – Dishayloo

+0

Tôi nghĩ vậy. Hoặc ít nhất tôi không thể tìm thấy bất cứ điều gì trong spec đó nói rằng Jetty nên sửa đổi loại nội dung trong trường hợp này. –

0

Dưới đây là một bộ lọc mà tôi đã viết để buộc mã 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

Để hỗ trợ câu trả lời Jesse Barnum, apache Wiki gợi ý rằng một bộ lọc có thể được sử dụng để điều khiển nhân vật mã hóa yêu cầu và phản hồi. Tuy nhiên, Tomcat 5.5 và up đi kèm với một SetCharacterEncodingFilter vì vậy nó có thể được tốt hơn để sử dụng thực hiện apache hơn để sử dụng Jesse (không có Jesse phạm tội). Việc triển khai tomcat chỉ thiết lập mã hóa ký tự theo yêu cầu, vì vậy việc sửa đổi có thể cần thiết để sử dụng bộ lọc làm phương tiện thiết lập ký tự được đặt trên phản hồi của tất cả các servlet.

Cụ thể, Tomcat có triển khai ví dụ ở đây:

5.x

webapps/servlets-ví dụ/WEB-INF/classes/bộ lọc/SetCharacterEncodingFilter.java

webapps/jsp- ví dụ/WEB-INF/lớp/bộ lọc/SetCharacterEncodingFilter.java

6.x

webapps/ví dụ/WEB-INF/classes/bộ lọc/SetCharacterEncodingFilter.java

7.x

Kể từ 7.0.20 bộ lọc trở thành hạng nhất và công dân đã được chuyển từ các ví dụ vào lõi Tomcat và có sẵn cho bất kỳ ứng dụng web nào mà không cần phải biên dịch và gói riêng nó. Xem tài liệu cho danh sách các bộ lọc do Tomcat cung cấp. Tên lớp là: org.apache.catalina.filters.SetCharacterEncodingFilter

trang này cho hơn: http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q3

Các vấn đề liên quan