1. 程式人生 > >Java Web亂碼分析及解決方案(二)——POST請求亂碼

Java Web亂碼分析及解決方案(二)——POST請求亂碼

引言

    GET請求的本質表現是將請求引數放在URL位址列中,form表單的Method為GET的情況,引數會被瀏覽器預設編碼,所以亂碼處理方案是一樣的。對於POST請求亂碼,解決起來要比GET簡單,我們關心的重點是在Request Body中。

請求亂碼——Method方式

使用Method方式傳送HTTP請求時,根據HTTP協議的規定,查詢引數應該在Request的Body中,例如在Chrome下可以看到URL中不含有查詢引數。

瀏覽器的編碼

瀏覽器對POST的Request Body編碼會採用頁面指定的編碼。這句話是我們聽到最多也是眾口相傳的,但是到底什麼是“頁面指定的編碼”?這個問題就涉及到我們的響應亂碼問題了,用句廢話就是頁面顯示時採用的編碼,通過瀏覽器右鍵可以看到的。但是如果頁面亂碼了,那麼頁面使用的編碼就是Content-Type裡面指定的編碼,如果Content-Type沒有指定,那麼就是meta標籤中指定的charset,這兩個編碼即影響了瀏覽器對Page頁面的解碼,又影響了POST請求對Request Body的編碼。詳細可以檢視meta標籤的http-equiv和Content-Type資料。

   HTTP包傳輸的是位元組碼,不會傳輸字元,所以,不管是GET還是POST都需要編碼。使用Chrome觀察Request的Http包時,我們會發現POST的資料被放在Form Date下面,而GET的資料放在Query String Parameters頁簽下面。在頁面沒有亂碼情況下,中文引數都會被預設解碼。切換到View Source下面可以看到提交的編碼引數。對於Request Body的長度,瀏覽器通過新增Content-Length報頭來標記位元組長度。

伺服器的解碼

Web容器對POST方法的解碼受request.setCharacterEncoding方法的影響。對於Tomcat容器,它的官方有這樣的說明:

How do I change how POST parameters areinterpreted?

POST requests should specify the encoding ofthe parameters and values they send. Since many clients fail to set an explicitencoding, the default is used (ISO-8859-1). In many cases this is not thepreferred interpretation so one can employ a javax.servlet.Filter to setrequest encodings. Writing such a filter is trivial.

翻譯過來就是:

   對於POST請求,客戶端應該明確的指明引數和值採用的編碼型別,但是許多客戶端並沒有這麼做,所以Tomcat會預設使用ISO-8859-1來解碼POST的引數和值。許多時候,我們可以使用一個Filter來設定Request的編碼,寫這麼個Filter是微不足道的一件小事。

簡單說就是Tomcat預設使用ISO-8859-1來解碼POST的引數和值,可以使用Filter來設定Request Body的字符集。方法就是呼叫request的setCharsetEncoding。

文件中並沒有說明客戶端應該如何明確說明POST請求的編碼型別,但是我想可能是通過Http協議的標準報頭:Accept-Charset來指定的吧。我們沒必要去做這個實驗,因為我們的程式絕對不應該把這件事完全託管給“不靠譜”的客戶端。

所以大多數情況下,我們會在getParameter方法前呼叫request的setCharacterEncoding方法。就像Tomcat文件中說的,用一個自己寫的或者官方提供的Filter完成這項工作就好了,Spring MVC提供的Filter也是簡單的呼叫了這個方法:

Spring MVC 字元編碼過濾器原始碼: 

@Override
   protected void doFilterInternal(
         HttpServletRequestrequest,HttpServletResponse response,FilterChain filterChain)
         throws ServletException,IOException {
 
      if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding()== null)){
         request.setCharacterEncoding(this.encoding);
         if (this.forceEncoding) {
            response.setCharacterEncoding(this.encoding);
         }
      }
      filterChain.doFilter(request, response);
   }

出現亂碼:

   POST方法出現亂碼時,首先確定下我們的頁面使用的是哪種編碼方案,然後呼叫request的setCharsetEncoding,一定要在getParameter前呼叫這個方法。

   如果仍然是亂碼通過這樣一個小實驗就能知道哪裡出錯了。

   (1)先獲取我們的亂碼,String param = req.getParameter("xx");

   (2)對param = new String(param.getBytes(“伺服器編碼”,“頁面編碼”));

   (3)伺服器編碼、頁面編碼要有“合理”的猜測,頁面編碼通過瀏覽器選單觀察,別相信自己寫的HTML標籤。伺服器編碼可以試驗ISO-8859-1和自己set的charset encoding,然後看看哪裡和自己設計的不一致,問題自然就清楚了。

總結:

POST方式的亂碼比GET處理要簡單,我們不用關心URL的編碼,也不用編碼解碼URL。POST亂碼,一般呼叫setCharsetEncoding就可以解決了。但是要注意的是,POST的資料一樣是經過編碼、解碼的!!只不過是不用手動進行而已。