以前一直使用resin-3.0.x作爲服務器,最近想升級到resin3.1,於是在官網上下載了resin3.1.7a,解壓配置一切正常,但把應用重新部署上去的時候就出了問題,以前一直正常的頁面,現在卻報錯:
500 Servlet Exception
<script type="text/javascript"><!----></script> [show] /index.jsp:1: contentType 'text/vnd.wap.wml; charset=utf-8' conflicts with previous value of contentType 'text/html; charset=UTF-8'. Check the .jsp and any included .jsp files for conflicts. 1: <%@page contentType="text/vnd.wap.wml; charset=utf-8"%> 2: <%@page import="java.util.*"%> 3: <%!
根據上面的提示,意思似乎是我在jsp裏面第一行設置的contentType是'text/vnd.wap.wml; charset=utf-8,和前面設置的'text/html; charset=UTF-8'不同導致衝突,但這個文件的第一行就是<%@pagecontentType="text/vnd.wap.wml;charset=utf-8" %>,根本沒有設置過'text/html; charset=UTF-8',這個提示真是讓人很摸不着頭腦。
後來想到可能是UTF8文件格式的問題,就用UE打開文件,另存了一次,選的是不帶BOM的UTF8格式的文件,這次就可以正常顯示了。但服務器上那麼多文件,不可能一個一個的改,還得想其他的辦法解決。在網上找了很久都沒有任何頭緒,似乎遇到這個問題的人很少。
最後實在是沒辦法,只好把源代碼下載下來研究一下看了,還真的找出了原因所在。
因爲resin在處理jsp文件的時候,會首先讀取前面的幾個字節來判斷文件的格式,如果第一個字節是0xef、第二個字節是0xbb、第三個字節是0xbf,那麼就認爲這個文件是UTF8格式,於是就自作主張的把ContentType設置成了text/html; charset=UTF-8,然後在後面的處理過程中,因爲jsp程序裏面會有設置ContentType的指令,遇到這個指令會發現和之前的text/html; charset=UTF-8不同,因此就拋出了異常。而如果沒有BOM格式的UTF8,前面就不會有那三個字節的標識,所以就不會被處理了。
相關代碼:
- case 0xef:
- if ((ch = stream.read()) != 0xbb) {
- stream.unread();
- stream.unread();
- }
- else if ((ch = stream.read()) != 0xbf) {
- throw error(L.l("Expected 0xbf in UTF-8 header. UTF-8 pages with the initial byte 0xbb expect 0xbf immediately following. The 0xbb 0xbf sequence is used by some application to suggest UTF-8 encoding without a directive."));
- }
- else {
- _parseState.setContentType("text/html; charset=UTF-8");
- _parseState.setPageEncoding("UTF-8");
- stream.setEncoding("UTF-8");
- }
- break;
判斷衝突的代碼:
- else if (CONTENT_TYPE.equals(name)) {
- String oldContentType = _parseState.getContentType();
- if (oldContentType != null && ! value.equals(oldContentType))
- throw error(L.l("contentType '{0}' conflicts with previous value of contentType '{1}'. Check the .jsp and any included .jsp files for conflicts.", value, oldContentType));
- _parseState.setContentType(value);
- String charEncoding = parseCharEncoding(value);
- if (charEncoding != null)
- parseState.setCharEncoding(charEncoding);
- }
真不明白爲什麼resin要這麼做呢,如果是web網站可能影響不大,contentType本來就是text/html,但如果是wap或者其他contentType的站點這麼“智能”的編碼判斷方式問題就比較麻煩了。
附:UTF-8 編碼的文件可以分爲no BOM 和 BOM兩種格式(轉載)
何謂BOM? "EF BB BF" 這三個字節就叫BOM,BOM的全稱叫做"Byte Order Mard".在utf-8文件中常用BOM來表明這個文件是UTF-8文件,而BOM的本意實在utf16中用來表示高低字節序列的。
在字節流之前有BOM表示採用低字節序列(低字節在前面),而utf8不用考慮字節序列,所以其實有無BOM都可以。
微軟的記事本 Word 等只能正確打開含BOM的UTF8文件,然後UltraEdit卻恰恰相反,回把BOMutf8文件 誤認爲ascii編碼。
UTF-8的BOM是 EFBBBF,因爲UE載入UTF-8文件會轉成Utf16,上述的EFBBBF 在Utf16中是FFFE(Unicode-LE的BOM),UltraEdit不認識BOM又加多一個BOM,所以有2個FFFE。
文件就被它破壞了。