jsp編碼問題

來源:互聯網
上載者:User

文章出處:http://www.blogjava.net/freeman1984/archive/2010/06/01/322465.html

原文出處:http://blog.csdn.net/yzhz 

一、問題:
        編碼問題是JAVA初學者在web開發過程中經常會遇到問題,網上也有大量相關的文章介紹,但其中很多文章並沒有對URL中使用了中文等非ASCII的字 符造成伺服器背景程式解析出現亂碼的問題作出準確的解釋和說明。本文將詳細介紹由於在URL中使用了中文等非ASCII的字元造成亂碼的問題。

1、在URL中中文字元通常出現在以下兩個地方:
(1)、Query String中的參數值,比如http://search.china.alibaba.com/search/offer_search.htm?keywords=中國
(2)、servlet path,比如:http://search.china.alibaba.com/selloffer/中國.html


2、出現亂碼問題的原因主要是以下幾方面:
(1)、瀏覽器:我們的用戶端(瀏覽器)本身並沒有遵循URI編碼的規範(http://www.w3.org/International/O-URL-code.html)。
(2)、Servlet伺服器:Servlet伺服器的沒有正確配置。
(3)、開發人員並不瞭解Servlet的規範和API的含義。

二、基礎知識:
1、一個http請求經過的幾個環節:
瀏覽器(ie firefox)【get/post】------------>Servlet伺服器------------------------------->瀏覽器顯示
                               編碼                 解碼成unicode,然後將顯示的內容編碼        解碼
(1) 瀏覽器把URL(以及post提交的內容)經過編碼後發送給伺服器。
(2) 這裡的Servlet伺服器實際上指的是由Servlet伺服器提供的servlet實現ServletRequestWrapper,不同應用伺服器的 servlet實現不同,這些servlet的實現把這些內容解碼轉換為unicode,處理完畢後,然後再把結果(即網頁)編碼返回給瀏覽器。
(3) 瀏覽器按照指定的編碼顯示該網頁。

        當對字串進行編碼和解碼的時候都涉及到字元集,通常使用的字元集為ISO8859-1、GBK、UTF-8、UNICODE。


2、URL的組成:
網域名稱:連接埠/contextPath/servletPath/pathInfo?queryString
說明:

1、ContextPath是在Servlet伺服器的設定檔中指定的。
對於weblogic:
contextPath是在應用的weblogic.xml中配置。
 <context-root>/</context-root>
 
對於tomcat:
contextPath是在server.xml中配置。
<Context path="/" docBase="D:/server/blog.war" debug="5" reloadable="true" crossContext="true"/>

對於jboos:
contextPath是在應用的jboss-web.xml中配置。
<jboss-web>
    <context-root>/</context-root>
</jboss-web>

2、ServletPath是在應用的web.xml中配置。
<servlet-mapping>
    <servlet-name>Example</servlet-name>
    <url-pattern>/example/*</url-pattern>
</servlet-mapping>

2、Servlet API
我們使用以下servlet API獲得URL的值及參數。
request.getParameter("name");         // 獲得queryString的參數值(來自於get和post),其值經過Servlet伺服器URL Decode過的
request.getPathInfo();                // 注意:pathinfo返回的字串是經過Servlet伺服器URL Decode過的。
requestURI = request.getRequestURI(); // 內容為:contextPath/servletPath/pathinfo 瀏覽器提交過來的未經處理資料,未被Servlet伺服器URL Decode過。


3、開發人員必須清楚的servlet規範:
(1) HttpServletRequest.setCharacterEncoding()方法 僅僅只適用於設定post提交的request body的編碼而不是設定get方法提交的queryString的編碼。該方法告訴應用伺服器應該採用什麼編碼解析post傳過來的內容。很多文章並沒 有說明這一點。
(2) HttpServletRequest.getPathInfo()返回的結果是由Servlet伺服器解碼(decode)過的。
(3) HttpServletRequest.getRequestURI()返回的字串沒有被Servlet伺服器decoded過。
(4) POST提交的資料是作為request body的一部分。
(5) 網頁的Http頭中ContentType("text/html; charset=GBK")的作用:
   (a) 告訴瀏覽器網頁中資料是什麼編碼;
   (b) 表單提交時,通常瀏覽器會根據ContentType指定的charset對錶單中的資料編碼,然後發送給伺服器的。
   這裡需要注意的是:這裡所說的ContentType是指http頭的ContentType,而不是在網頁中meta中的ContentType。


三、下面我們分別從瀏覽器和應用伺服器來舉例說明:
URL:http://localhost:8080/example/中國?name=中國
漢字   編碼      二進位表示
中國   UTF-8     0xe4 0xb8 0xad 0xe5 0x9b 0xbd[-28, -72, -83, -27, -101, -67]
中國   GBK       0xd6 0xd0 0xb9 0xfa[-42, -48, -71, -6]
中國   ISO8859-1 0x3f,0x3f[63, 63]資訊失去


(一)、瀏覽器
1、GET方式提交,瀏覽器會對URL進行URL encode,然後發送給伺服器。
(1) 對於中文IE,如果在進階選項中選中總以UTF-8發送(預設),則PathInfo是URL Encode是按照UTF-8編碼,QueryString是按照GBK編碼。
http://localhost:8080/example/中國?name=中國
實際上提交是:
GET /example/%E4%B8%AD%E5%9B%BD?name=%D6%D0%B9%FA

(1) 對於中文IE,如果在進階選項中取消總以UTF-8發送,則PathInfo和QueryString是URL encode按照GBK編碼。
實際上提交是:
GET /example/%D6%D0%B9%FA?name=%D6%D0%B9%FA

(3) 對於中文firefox,則pathInfo和queryString都是URL encode按照GBK編碼。
實際上提交是:
GET /example/%D6%D0%B9%FA?name=%D6%D0%B9%FA

很顯然,不同的瀏覽器以及同一瀏覽器的不同設定,會影響最終URL中PathInfo的編碼。對於中文的IE和FIREFOX都是採用GBK編碼QueryString。

小結:解決方案:
1、URL中如果含有中文等非ASCII字元,則瀏覽器會對它們進行URLEncode。為了避免瀏覽器採用了我們不希望的編碼,所以最好不要在URL中直接使用非ASCII字元,而採用URL Encode編碼過的字串%.
比如:
URL:http://localhost:8080/example/中國?name=中國
建議:
URL:http://localhost:8080/example/%D6%D0%B9%FA?name=%D6%D0%B9%FA

2、我們建議URL中PathInfo和QueryString採用相同的編碼,這樣對伺服器端處理的時候會更加簡單。

2、還有一個問題,我發現很多程式員並不明白URL Encode是需要指定字元集的。不明白的人可以看看這篇文檔:http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/net/URLEncoder.html

2、 POST提交
        對於POST方式,表單中的參數值對是通過request body發送給伺服器,此時瀏覽器會根據網頁的ContentType("text/html; charset=GBK")中指定的編碼進行對錶單中的資料進行編碼,然後發給伺服器。
在伺服器端的程式中我們可以通過Request.setCharacterEncoding() 設定編碼,然後通過request.getParameter獲得正確的資料。

解決方案:
1、從最簡單,所需代價最小來看,我們對URL以及網頁中的編碼使用統一的編碼對我們來說是比較合適的。
如果不使用統一編碼的話,我們就需要在程式中做一些編碼轉換的事情。這也是我們為什麼看到有網路上大量的資料介紹如何對亂碼進行處理,其中很多解決方案都只是一時的權宜之計,沒有從根本上解決問題。


(二)、Servlet伺服器
        Servlet伺服器實現的Servlet遇到URL和POST提交的資料中含有%的字串,它會按照指定的字元集解碼。下面兩個Servlet方法返回的結果都是經過解碼的:
request.getParameter("name"); 
request.getPathInfo();

這裡所說的"指定的字元集"是在應用伺服器的設定檔中配置。

(1) tomcat伺服器
對於tomcat伺服器,該檔案是server.xml
<Connector port="8080" protocol="HTTP/1.1" 
               maxThreads="150" connectionTimeout="20000" 
               redirectPort="8443" URIEncoding="GBK"/>
URIEncoding告訴伺服器servlet解碼URL時採用的編碼。

<Connector port="8080" ... useBodyEncodingForURI="true" />
useBodyEncodingForURI告訴伺服器解碼URL時候需要採用request body指定的編碼。

(2) weblogic伺服器
對於weblogic伺服器,該檔案是weblogic.xml 
<input-charset>
  <java-charset-name>GBK</java-charset-name>
</input-charset>

(三)瀏覽器顯示
        瀏覽器根據http頭中的ContentType("text/html; charset=GBK"),指定的字元集來解碼伺服器發送過來的位元組流。我們可以調用 HttpServletResponse.setContentType()設定http頭的ContentType。

總結:
1、URL中的PathInfo和QueryString字串的編碼和解碼是由瀏覽器和應用伺服器的配置決定的,我們的程式不能設定,不要期望用request.setCharacterEncoding()方法能設定URL中參數值解碼時的字元集。
所以我們建議URL中不要使用中文等非ASCII字元,如果含有非ASCII字元的話要使用URLEncode編碼一下,比如:
http://localhost:8080/example1/example/中國
正確的寫法:
http://localhost:8080/example1/example/%E4%B8%AD%E5%9B%BD
並且我們建議URL中不要在PathInfo和QueryString同時使用非ASCII字元,比如
http://localhost:8080/example1/example/中國?name=中國
原因很簡單:不同瀏覽器對URL中PathInfo和QueryString編碼時採用的字元集不同,但應用伺服器對URL通常會採用相同的字元集來解碼。

2、我們建議URL中的URL Encode編碼的字元集和網頁的contentType的字元集採用相同的字元集,這樣程式的實現就很簡單,不用做複雜的編碼轉換。 

-----自己的一些總結:

jsp編碼相關-----------------------------
1. pageEncoding: 只是指明了 JSP 頁面本身的編碼格式,跟頁面顯示的編碼沒有關係;
    容器在讀取(檔案)或者(資料庫)或者(字串常量)時將起轉化為內部使用的 Unicode,而頁面顯示的時候將
    內部的Unicode轉換為contentType指定的編碼後顯示頁面內容;
    如果pageEncoding屬性存在,那麼JSP頁面的字元編碼方式就由pageEncoding決定,
    否則就由contentType屬性中的charset決定,如果charset也不存在,JSP頁面的字元編碼方式就採用
    預設的ISO-8859-1。
    2. contentType: 指定了MIME類型和JSP頁面回應時的字元編碼方式。MIME類型的預設值是“text/html”; 
    字元編碼方式的預設值是“ISO-8859-1”. MIME類型和字元編碼方式由分號隔開;
    3. pageEncoding和contentType的關係:
        1. pageEncoding的內容只是用於jsp輸出時的編碼,不會作為header發出去的; 是告訴web Server 
            jsp頁面按照什麼編碼輸出,即web伺服器輸出的響應流的編碼;
       2. 第一階段是jsp編譯成.java,它會根據pageEncoding的設定讀取jsp,結果是由指定的編碼方案翻譯
            成統一的UTF-8 JAVA源碼(即.java).
       3. 第二階段是由JAVAC的JAVA源碼至java byteCode的編譯,不論JSP編寫時候用的是什麼編碼方案,
            經過這個階段的結果全部是UTF-8的encoding的java源碼.JAVAC用UTF-8的encoding讀取
            java源碼,編譯成UTF-8 encoding的二進位碼(即.class),這是JVM對常數字串在二進位碼
            (java encoding)內表達的規範.
       4. 第三階段是Tomcat(或其的application container)載入和執行階段二的來的JAVA二進位碼,
            輸出的結果,也就是在用戶端見到的,這時隱藏在階段一和階段二的參數contentType就發揮了功效    
    4. 和contentType效果一樣的設定方式還有 html頁面charset, response.setCharacterEncoding(), 
        response.setContentType(),response.setHeader(); response.setContentType(),
        response.setHeader();優先順序最好,其次是response.setCharacterEncoding();再者是
        <%@page contentType="text/html; chareset=gbk"%>,最後是<meta http-equiv="content-type" 
        content="text/html; charset=gb2312" />.
    5. web頁面輸入編碼: 在設定頁面編碼<%@page contentType="text/html; chareset=gbk"%>的同時,也就指定了頁面的輸入編碼;
        如果頁面的顯示被設定為UTF-8,那麼使用者所有的頁面輸入都會按照 UTF-8 編碼; 伺服器端程式在讀
        取表單輸入之前要設定輸入編碼;
        表單被提交後,瀏覽器會將表單欄位值轉換為指定字元集對應的位元組值,然後根據 HTTP 標準 URL
        編碼方案對結果位元組進行編碼.但是頁面需要告訴伺服器當前頁面的編碼方式;
        request.setCharacterEncoding(),能修改Serverlet擷取請求的編碼,response.setCharacterEncoding(),
        能修改Serverlet返回結果的編碼. 
    6;對於get方法表單通過設定 response.setCharacterEncoding(), 和request.setCharacterEncoding("utf-8")無效,
 get有長度限制,最長不超過2048位元組(1024個漢字)
      

注意編碼要在擷取寫通道之前設定:
response.setContentType("text/html;charset=gbk");
PrintWriter out = response.getWriter();
如果寫成如下將導致編碼設定無效:
PrintWriter out = response.getWriter();
response.setContentType("text/html;charset=gbk");
在設定編碼之前擷取度通道將導致後期設定編碼無效
System.out.println("997:"+request.getParameter("ta"));
request.setCharacterEncoding("utf-8");
System.out.println("999:"+request.getParameter("ta"));
正確如下:
request.setCharacterEncoding("utf-8");
System.out.println("999:"+request.getParameter("ta"));
-----------------------------2--

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.