request.setCharacterEncoding 關於編碼
概述
從Servlet2.3開始,支援用戶端內容協商。服務端內容協商,很早就有,服務端在返回的資料中通過Content-Type來指定返回的資料內容。在REST叫囂的背景下,用戶端也需要協商:例如通過PUT方法提交一段XML或JSON資料來更新服務端的一個對象。用戶端可通過URL尾碼名.xml或.json的方式來告訴服務端提交的資料類型;也可通過HTTP頭的Content-Type來告之服務端提交的資料類型。
關於該問題的Blog
【1】給出了兩個辦法
http://forum.springsource.org/showthread.php?t=14063
Hi,
I am woking on a site that receives input in CJK .
This may be a naive question:
I am using org.springframework.web.servlet.DispatcherServlet as my servlet and I need to set CharacterEnconding on the HttpServletRequest.
I looked into the source code and I relaized there is no code that calls setCharacterEncoding
I dig into the forum and found 2 solutions:
1. Use the CharacterEncodingFilter
2. Override DispatcherServlet.doService as:
public class MyServlet extends DispatcherServlet {
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding( "UTF-8" );
super.doService( request , response ) ;
}
}
I tried to do #1 for it seems to be more desirable (cleaner) solution.
in web.xml I added:
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFi lter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
But it doesn't seem to work.
Could someone tell me how to do that? Use CharacterEncodingFilter?
Thanks in advance.
【2】注意事項
http://www.junlu.com/msg/125726.html
With the 2.3 servet API, there is a new method:
request.setCharacterEncoding(String encoding)
通過Content-Type告訴服務端當前請求編碼:
This lets you tell the server a request's character encoding.
(例如HTTP頭: Content-Type: text/html; charset=UTF-8
Content-Type:application/x-www-form-urlencoded; charset=UTF-8)
It is critical that setCharacterEncoding is called BEFORE any
request.getParameter is called (or getReader). Otherwise, you are at the
mercy of the appserver for what you get back on the getParameter call.
For example, if setCharacterEncoding is not called, you could get a null
value back on getParameter("foo").
解決該問題:用戶端協商編碼方式
用戶端協商http請求編碼方式代碼
- private String negotiateCharacterEncoding(HttpServletRequest request,Map<String,String> outParam) {
- String clientEncoding = request.getCharacterEncoding();//HTTP標準:用戶端聲稱的編碼(但是目前大多數瀏覽器並未實施該標準)
- outParam.put("point","HTTP標準");
- //協商過程:
- //1. 通過HTTP標準指定用戶端編碼;(在HTTP頭中設定:Content-Type =[text/html; charset=UTF-8])
- //2. 通過自訂HTTP頭(Client-Charset)指定用戶端編碼;
- //3. 通過自訂HTTP查詢參數(Client-Charset)指定用戶端編碼(只針對HTTP-GET方法)。(以免動態script標籤發起請求時設定不了HTTP頭;編碼數值都是英文字元,提取編碼數值跟編碼無關。)
- //4. 如果所有的協商都沒有,那麼服務端強制使用配置:defaultEncoding
- //5. 如果服務端沒有配置defaultEncoding,那麼使用容器預設的ISO-8...(如果上述指定的編碼不被支援,那麼依然使用容器預設的)
- if(clientEncoding==null || clientEncoding.trim().equals("")) {
- clientEncoding = request.getHeader("Client-Charset");
- outParam.put("point","自訂HTTP頭");
- if(clientEncoding==null || clientEncoding.trim().equals("")) {
- // clientEncoding = request.getParameter("Client-Charset");//不能通過該方式提取Client-Charset參數
- //request.setCharacterEncoding(encoding);發揮作用的前提是:調用setCharacterEncoding之前不能執行任何request.getParameter
- if("GET".equalsIgnoreCase(request.getMethod())) {
- String queryString = request.getQueryString();
- if(queryString!=null && !queryString.equals("")) {
- //定位參數[Client-Charset]的起始和終止位置
- int startIndex = queryString.indexOf("Client-Charset=");
- int endIndex = -1;
- if(startIndex!=-1) {
- startIndex = startIndex+"Client-Charset=".length();
- endIndex = queryString.indexOf("&", startIndex);
- if(endIndex==-1) {//Client-Charset是最後一個參數
- int sessionidIndex = queryString.indexOf(";", startIndex);//去掉基於URL的SessionID
- if(sessionidIndex!=-1) {
- endIndex = sessionidIndex;
- } else {
- endIndex = queryString.length();
- }
- }
- }
- if(startIndex<endIndex) {
- clientEncoding = queryString.substring(startIndex, endIndex);
- outParam.put("point","自訂HTTP查詢參數");
- }
- }
- }
- }
- if(clientEncoding==null || clientEncoding.trim().equals("")) {
- clientEncoding = defaultEncoding;
- outParam.put("point","服務端配置");
- }
-
- }
- return clientEncoding;
- }
設定編碼方式代碼
- if (encoding != null) {
- try {
- request.setCharacterEncoding(encoding);//註:被強制認為是GBK編碼,好處在於用戶端在提交GET請求時不再需要做URLEncode處理了。不好的是,如果用戶端提交以UTF-8的編碼,則編碼出錯了。
- //http://www.junlu.com/msg/125726.html
- } catch (Exception e) {
- log.error("Error setting character encoding to '" + encoding
- + "' - ignoring.", e);
- }
- }
測試案例
/modifyListener_test.htm?nick=繁體昵稱衝頂&mobile=13812345678
/modifyListener_test.htm?nick=%B7%B1%F3%77%EA%C7%B7%51%D0%6E%ED%94&mobile=13812345678&Client-Charset=GBK
/modifyListener_test.htm?nick=%E7%B9%81%E9%AB%94%E6%98%B5%E7%A8%B1%E8%A1%9D%E9%A0%82&mobile=13812345678&Client-Charset=UTF-8
/modifyListener_test.htm?nick=濤&mobile=13812345678
/modifyListener_test.htm?nick=%CC%CE&mobile=13812345678&Client-Charset=GBK
/modifyListener_test.htm?nick=%CC%CE&mobile=13812345678
/modifyListener_test.htm?nick=%E6%B6%9B&mobile=13812345678&Client-Charset=UTF-8
/modifyListener_test.htm?nick=%E6%B6%9B&mobile=13812345678 //出錯:輸入是UTF-8,卻被伺服器強製為GBK (###nick=娑?,mobile=13812345678)
/modifyListener_test.htm?nick=%E6%B6%9B&mobile=13812345678&Client-Charset=UTF-8;12345
/modifyListener_test.htm?Client-Charset=UTF-8&nick=%E6%B6%9B&mobile=13812345678;12345 //nick=濤,mobile=13812345678;12345
/modifyListener_test.htm?nick=%E6%B6%9B&mobile=13812345678
並設定包頭:
Content-Type: text/html; charset=UTF-8
或
Content-Type: charset=UTF-8
//協商或配置的編碼:UTF-8,協商源:HTTP標準
問題回覆:
Passport有一個全域的Filter,強制所有的HTTP請求的編碼為GBK,所以支援不了URLEncode(UTF-8)。我想了這麼些辦法,都不行:
1、 把這個全域的Filter去掉,不強製為GBK,現有線上的那些沒有編碼的東西支援不了。
這樣設定,對於那些沒有編碼的資料,比如:http://localhost/modifyListener_test.htm?nick=繁體昵稱衝頂&mobile=13812345678 提取的nick則會出錯。因此會影響線上其他地方。
2、 手動從GBK再轉UTF-8,部分資料能支援,有些不支援。(GBK和UTF-8字元集畢竟不是包含與被包含的關係,其中有衝突的部分)
New String(nick.getBytes(“GBK”),”UTF-8”) 對於http://localhost/modifyListener_test.htm?nick=繁體昵稱衝頂&mobile=13812345678能轉換出“繁體昵稱衝頂”;但對於
http://localhost/modifyListener_test.htm?nick=濤&mobile=13812345678 其中“波濤”的“濤”則轉換失敗。
現在一個可行的解決辦法是通過HTTP頭協商,需要麻煩你那邊在請求中加一個參數:
1、 在HTTP頭部增加參數:Content-Type,並設定數值:charset=UTF-8 (備忘:在HTTP頭裡設定Client-Charset參數,數值為UTF-8也行。但前提是Content-Type沒被設定為其他)
這樣設定後,nick就可以支援URLEncode(UTF-8)了。
2、 對於動態script標籤的請求,由於無法設定HTTP頭,後台支援查詢參數:Client-Charset
比如:這樣提交HTTP請求: nick=%E6%B6%9B&mobile=13812345678&Client-Charset=UTF-8