常见中文乱码问题
日常工作中,中文亂碼算是比較常見的問題了,大家或多或少都遇到過。這里簡單分析下我遇到過的亂碼場景,錯誤和遺漏之處,歡迎大家補充、糾正和交流。
?
一、常見亂碼問題
1.1 Ajax請求中文參數(shù)亂碼
? ? ? 發(fā)送Ajax請求時,如果參數(shù)中有中文,服務端獲取到參數(shù)后,有可能亂碼。
1.2 外聯(lián)js代碼中文字符亂碼
????? 外聯(lián)方式引入js文件,如果js的代碼中有中文,有可能亂碼。
下面就來依次分析這兩個場景。
?
二、Ajax請求中文參數(shù)亂碼
? ? ? Ajax請求分為GET和POST兩種方式,對于這2種方式,Webx框架的處理不盡相同。此外,在客戶端瀏覽器的處理上,也有可能出現(xiàn)一些中文編碼字符集的不確定性。
?
? ? ?2.1 知識點
? ? ?首先介紹下encodeURIComponent('xxx'),這個js原生函數(shù)采用UTF-8編碼,js代碼中對中文字符進行顯式編碼時,基本上用的都是該函數(shù)。
???? 接下來介紹下編碼的基本知識,在此copy一下Webx文檔的一段話,如下。
? ? ?2.1.1 GET請求
? ? ?對于GET方式的Ajax請求,如果未顯式執(zhí)行encodeURIComponent對中文參數(shù)進行編碼,那么瀏覽器會根據“輸出字符集”對中文字符進行編碼,Webx默認配置的是GBK輸出字符集,也就是說瀏覽器極有可能采用GBK字符集進行中文字符編碼。當前,也不排除個別瀏覽器在實現(xiàn)上不是這個套路。如果使用了某些JS庫的Ajax組件,JS庫也有可能額外搞一些動作。而對于英文操作系統(tǒng),由于我沒有測試過,也不確定是否會影響到瀏覽器的編碼字符集。因此存在著一定的編碼字符集不確定性。
? ? ??在服務端對GET請求的處理過程中,Webx框架沒有采用Servlet引擎的解碼方式,而是另起爐灶,特殊進行了參數(shù)解析和字符解碼。需要特別說明的是,Webx在此之前,統(tǒng)一設置了CharacterEncoding,Webx默認配置的是GBK輸入字符集。GET請求參數(shù)解析和字符解碼的關鍵代碼如下圖所示。
? ?
key = decode(key); value = decode(value);? ? 詳細代碼可參見Webx3的com.alibaba.citrus.service.requestcontext.parser.impl.ParameterParserImpl類,以構造函數(shù)為入口進行閱讀。
?
? ? 2.1.2 POST請求
? ? 對于POST方式的Ajax請求,字符編碼一律采用UTF-8,這應該是Ajax的一個規(guī)范。在KISSY的代碼和Webx的文檔中都有闡述,Ajax規(guī)范還有待進一步研究確認。?
??? 對于非Ajax的正常表單提交,字符編碼會采用web頁面的字符集,對于淘寶的頁面來講,通常就是GBK。
? ? 在服務端對POST請求的處理過程中,Webx框架采用了Servlet引擎提供的解碼方式。仍然需要特別說明的是,Webx在Servlet引擎解碼之前,也統(tǒng)一設置了HttpServletRequest的CharacterEncoding,即“輸入字符集”,Webx默認配置的是GBK,與GET請求設置CharacterEncoding是在同一個地方。如下所示。
// 試圖從queryString中取得inputCharset String queryString = getRequest().getQueryString(); String inputCharset = locale.getCharset().name(); if (queryString != null) {Matcher matcher = inputCharsetPattern.matcher(queryString);if (matcher.find()) {String charset = matcher.group(1);if (LocaleUtil.isCharsetSupported(charset)) {inputCharset = charset;}} } getRequest().setCharacterEncoding(inputCharset);詳細代碼可參見Webx3的com.alibaba.citrus.service.requestcontext.locale.impl.SetLocaleRequestContextImpl類,以prepare方法為入口進行閱讀。
?
? ? 2.2 亂碼原因
? ??由以上知識點可知,我們通常遇到的亂碼一般是由于客戶端對中文采用了UTF-8編碼,而服務端采用GBK解碼導致。
?
? ? 2.3 解決方法
??? 2.3.1 對于GET請求產生的亂碼,通常需要做兩件事情。第一,使用encodeURIComponent對中文字符進行編碼,消除編碼字符集的不確定性。第二,需要在url中額外增加?_input_charset?參數(shù),值為UTF-8,這個參數(shù)是Webx預留的參數(shù),可以優(yōu)先設置本次請求的解碼字符集,核心代碼同2.1.2的代碼貼圖,這里補充一下webx.xml的配置和匹配_input_charset 參數(shù)的正則Pattern,如下所示。
<set-locale defaultLocale="zh_CN" defaultCharset="GBK" /> String INPUT_CHARSET_PARAM_DEFAULT = "_input_charset";inputCharsetParam = defaultIfNull(inputCharsetParam, INPUT_CHARSET_PARAM_DEFAULT); inputCharsetPattern = Pattern.compile(inputCharsetParam + "=([w-]+)");??? 這里還有另外一種處理方式,即尋找第三方js庫,提供GBK編碼的encodeUri函數(shù),可以免去url中的_input_charset 參數(shù)。
?
??? 2.3.2 對于POST請求產生的亂碼,通常只需要在url(請注意是url,不是表單參數(shù))中額外增加?_input_charset 參數(shù),值為UTF-8即可。
??? 如果這樣處理仍然有問題,并且使用的是KISSY庫的Ajax組件,那么可以先使用encodeURIComponent對中文字符進行編碼,然后再發(fā)送Ajax請求,這時候url中是否有_input_charset 參數(shù)已經無關緊要。之后,服務端業(yè)務代碼要顯式執(zhí)行URLDecoder.decode("xxx","UTF-8"),即可獲取到正確的中文字符。這里面發(fā)生了一些有趣的事情,簡單YY下。在顯式執(zhí)行encodeURIComponent之后,發(fā)送Ajax請求之前,KISSY的Ajax組件又額外進行了一次encodeURIComponent。以“測試”舉例,經過一次encodeURIComponent處理后的值是“測試”,再經過一次encodeURIComponent,值變成了“%25E6%25B5%258B%25E8%25AF%2595”,這樣無論Webx框架采用何種輸入字符集,Servlet引擎解碼后,值都會恢復成“測試”,之后業(yè)務代碼再顯式執(zhí)行URLDecoder # decode,就拿到中文字符了。
? ? KISSY額外的一次encodeURIComponent,發(fā)生在對傳入的表單數(shù)組進行S.param的時候,可以參看源碼。
?
三、外聯(lián)js代碼中文字符亂碼
???? 3.1 知識點
???? web頁面引用外聯(lián)JS,若未加特殊處理,一般會按照web頁面的字符集對外聯(lián)JS進行解碼。
???? 瀏覽器解碼的字符集,一般按照以下優(yōu)先級判斷。http?header(”content-type:text/html; charset=xxx”)優(yōu)先級最高,如http-header未指定則依據html?<META http-equiv=”content-type” content=”text/html; charset=xxx”>,如果http-header和html-meta都沒有指定,那么一般就是依據文件的BOM編碼格式。
???? 3.2 亂碼原因
???? 我遇到的亂碼都是發(fā)生在daily環(huán)境。
???? 淘寶的web頁面基本都是GBK字符集。
???? 淘寶外聯(lián)的JS,daily環(huán)境放在assets測試服務器上,線上環(huán)境放在tbcdn上,編碼字符集由JS文件的BOM決定。
???? 前端同學編寫JS代碼,在保存時一般都采用UTF-8編碼。
???? 因此,用GBK解析UTF-8編碼的中文字符,就出現(xiàn)亂碼了。
???? 3.3 解決方法
???? 3.3.1 對script單獨設置解碼字符集,<script type=”text/javascript” src=”xxx.js” charset=”UTF-8″></script>,這樣處理后,該script的解碼就按照指定的charset進行。
???? 3.3.2 前端采用GBK字符集保存代碼并提交。這個應該不太可行,前端采用UTF-8編碼應該有一些原因,至于是對壓縮有影響,還是其它的原因,有待深入探究。
???? 3.3.3 采用ucool等工具作代理,并將代理文件以GBK字符集保存。
???? 3.3.4 直接引用壓縮之后的-min.js,這樣會給debug帶來困難,但肯定不會有亂碼問題。因為淘寶使用的壓縮工具,會將所有的中文字符替換成Unicode編碼。因為線上引用的都是-min.js,所以就沒有亂碼問題。由此可見,壓縮JS不單是縮短客戶端下載時間,也能夠避免亂碼問題。
總結
- 上一篇: 电视照明用LED光源显色性TLCI计算软
- 下一篇: OBS屏幕录制软件