如何解決Java WEB應用中的亂碼問題

Creative Commons License
本作品採用知識共享署名-非商業性使用-相同方式共享 2.5 中國大陸許可協議進行許可。

 

 

當我們通過Java程序員的視角來瀏覽網頁時會發現:一方面用戶端瀏覽器(IE或Firefox)以表單或鏈接的方式提交HTTP請求同時又處理HTTP服務器發出的響應數據,將其中的數據流(HTML數據或其它種類的數據)以適當的方式展示給使用者瀏覽。另一方面在Java WEB應用服務器上,一個HTTP請求可以由一個Servlet類或一個JSP網頁來處理,請求數據來自於HttpServletRequest,響應數據發送至HttpServletResponse。通過用戶端提交請求、服務器端處理請求、服務器端返回響應數據以及用戶端處理響應數據四個步驟組成了一次HTTP請求的全部過程。數據在這四個重要環節中進行傳輸時,都將以指定的編碼方式進行編碼或解碼。如果處理不當就會出現亂碼問題。

用戶端的處理

當用戶端發出一個HTTP請求時,一個如下格式的數據將發送給服務器端:

<request-line>
<headers>
<CRLF>
[<request-body><CRLF>]

關於HTTP請求的格式,可以在HTTP協議與HTML表單(再談GET與POST的區別)中瞭解更多的內容。
在此,request-line與request-body均需要進行相應的編碼處理。

request-line的編碼處理

request-line中的URL部分必須以application/x-www-form-urlencoded方式編碼。編碼時使用的字符集是當前網頁在瀏覽器上顯示時所使用的字符集。

JDK中專門有兩個類處理application/x-www-form-urlencoded類型的數據,它們是URLEncoder及URLDecoder。當網頁上的數據需要手動進行URLEncoding處理時,可使用URLEncoder類完成編碼工作。需要手動進行URLEncoding處理的位置包括:

  • 鏈接(<a></a>)中的href標籤屬性;
  • 以POST方式提交的表單(<form></form>)中的action標籤屬性。

例如,網頁上不應該產生這樣的鏈接:

正確的寫法是:

爲此,方案之一可以在JSP網頁上使用腳本化語言進行URLEncoding處理。如:

request-body的編碼處理

request-body只有在POST提交的方式下才會產生。request-body的編碼方式由表單的enctype標籤屬性指定,同request-line一樣,編碼request-body時使用的字符集也是當前網頁在瀏覽器上顯示時所使用的字符集。request-body的編碼過程由客戶端瀏覽器自動完成,不需要額外的編程處理。

服務器的處理

相對於用戶端,服務器端在接收到HTTP請求時提供了兩種處理請求數據的方式:自動處理與不處理。
服務器一般會自動處理application/x-www-form-urlencoded類型的數據(包括request-line及request-body中的數據),就servlet(Servlet類或JSP網頁)而言,可以通過request對象的getParameter()或getParameterValues()取得這些數據。對於除此以外的其它MIME類型的數據,HTTP服務器則是將處理的過程直接交到了與HTTP請求相對應的servlet(Servlet類或JSP網頁)身上。
例如用戶端有以下表單被提交:

  

表單提交時經服務器端自動處理後與checkUser.html相對應的servlet(Servlet類或JSP網頁)可以通過下面的方式取得數據:

默認情況下,服務器對於接收到的application/x-www-form-urlencoded類型數據進行字符集爲ISO-8859-1的URLDecoding處理,經過處理之後的字符串內碼爲ISO-8859-1。對於沒有附加任何設置的HTTP服務器而言,我們的servlet在取得數據之後必須進行相應的解碼處理,生成內碼爲UTF-16(unicode)的字符串。
例如對於用戶端請求數據中以UTF-8字符集進行URLEncoding的數據,servlet需要進行如下方式的解碼:

爲了避免這種額外的編碼/解碼處理,也就是說讓服務器瞭解到用戶端在URLEncoding時所使用的字符集,從而直接進行相應字符集的URLDecoding處理,不同的HTTP服務器提供了不同的解決方案。
以Tomcat爲例,Tomcat自動解碼request-line的處理方式由Tomcat的配置文件server.xml指定。在server.xml中的Connector標籤中提供了URIEncoding標籤屬性,只要爲其指定解碼用的字符集,Tomcat就會自動解碼request-line中經過application/x-www-form-urlencoded編碼處理的數據。例如:

<Connector connectionTimeout="40000" port="8080" protocol="HTTP/1.1"
        URIEncoding="UTF-8" redirectPort="8443"/>

Tomcat自動解碼request-body的處理方式是設置request的characterEncoding值。如:

request.setCharacterEncoding("UTF-8");

但是這一操作必須提前在filter中完成,在servlet中使用此方法已經不起作用了。filter的例子如下:

我們可以在web.xml使用這個filter。web.xml的相應配置如下:

通過上述兩種方式的預處理,在servlet中取出的數據可以不必進行ISO-8859-1解碼而直接使用。

字符集的選擇

在處理application/x-www-form-urlencoded類型的數據過程中,需要注意的另一個問題是字符集的選擇問題。如上所述,不論是request-line還是request-body,其URLEncoding所使用的字符集都是當前網頁在瀏覽器上顯示時所使用的字符集。而這個信息又是HTTP服務器端生成HTML網頁時,在HTTP響應中提供的。
當HTTP服務器接收到一個HTTP請求時,服務器總是需要向用戶端發送一個HTTP響應。HTTP響應數據與HTTP請求數據格式相同,同樣由以下幾個部分組成:

<response-line>
<headers>
<CRLF>
[<response-body><CRLF>]

下面描述的是請求一個HTML網頁數據時服務器的響應信息:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=UTF-8
Content-Length: 265
Date: Thu, 17 Dec 2009 05:20:36 GMT

 

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>test</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
[End]

其中headers的Content-Type指定了數據流的數據格式以及顯示用的字符集。這一指標可以通過以下幾種方式指定:
1. HTML網頁
在HTML網頁的<head></head>中存在多個<meta/>標籤,其中可以設置Content-Type。例如:

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

2. JSP網頁
JSP網頁中,除了<meta/>標籤之外,還需要在JSP網頁頭部設置如下代碼:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

3. Servlet類
如果要在Servlet類中通過response向用戶端傳送HTML數據,需要在傳送前指定Content-Type。代碼如下:

response.setContentType("text/html;charset=UTF-8");

通過上述三種方式,可以確保響應數據傳送到用戶端瀏覽器時,瀏覽器使用正確解碼方式和字符集顯示其內容。

結束語

總之,Content-Type是聯繫用戶端與服務器端的紐帶,通過這一指標,雙方得以對相關的數據進行正確的編碼與解碼。只要瞭解了Content-Type的作用以及使用方法,亂碼問題就會迎刃而解。

發佈了27 篇原創文章 · 獲贊 15 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章