之前其實遇到過fmt:formatDate標簽導致頁面中文亂碼的問題,但是當時是我同事修改了程序解決的,我沒有自己研究。昨天需要用到fmt標簽了,想起來這個問題,嘗試重現了一下,在Resin上是正常的,在WebSphere上是亂碼的,于是就研究研究吧。
由于歷史原因,我們現在使用的軟件產品、開源框架都比較舊,列在下面:
J2EE Web: 2.3
JSTL: 1.0.6, 需要JSP 1.2 的Web容器支持
生產環境:JDk 1.4.2, WebSphere: 5.1.1.19,支持Servlet 2.3, JSP 1.2, EJB 2.0
開發環境:JDK 1.5.0(javac時編譯為1.4版本), Resin: 3.2.1
文件編碼及頁面ContentType中的編碼:UTF-8
首先,使用Fiddler跟蹤一下,發現頁面響應的編碼不再是我在頁面設置的UTF-8了,而是變成了GB13080。根據JSTL的說明文檔,很多fmt標簽都會調用fmt:setLocale,比如fmt:formatDate, fmt:formatNumber等。而fmt:setLocale會通過調用ServletResponse.setLocale方法來設置響應的Locale。在Servlet 2.3規范中,設置響應的Locale會影響頁面的編碼,同時規范中也說到,如果調用ServletResponse.setContentType,并且在參數中指明charset,那么會使用charset去覆蓋由于ServletResponse.setLocale導致的編碼。
問題的原因比較清楚了,解決辦法有這么幾種
1. 在調用了fmt標簽之后,通過response.setContentType("text/html; charset=UTF-8");來重設頁面編碼。但是這是不現實的,也是很傻的
2. 使用Filter設置Response的編碼。比如org.springframework.web.filter.CharacterEncodingFilter。Sorry,Servlet 2.3規范中,僅有getCharacterEncoding(),沒有setCharacterEncoding。所以在org.springframework.web.filter.CharacterEncodingFilter的代碼中,有一個靜態變量,就是用來檢測Web容器是否提供setCharacterEncoding編碼的:
private final static boolean responseSetCharacterEncodingAvailable = ClassUtils.hasMethod(
HttpServletResponse.class, "setCharacterEncoding", new Class[] {String.class});
3. 設置Locale和編碼的對照。還是不行,因為從Servlet 2.4 才開始提供Locale和編碼的映射關系,在web.xml里面:
<locale-encoding-mapping-list>
<locale-encoding-mapping>
<locale>ja</locale>
<encoding>Shift_JIS</encoding>
</locale-encoding-mapping>
</locale-encoding-mapping-list>
方法1太傻,即使它能解決這個問題,也不能真的用這種方法;方法2和方法3在我們現在環境中不支持。
所以,最后,放棄了fmt,使用Struts的bean:write。
JSTL,挺好的東西,俺只有望洋興嘆的份了~~~