之前開發過程中遇到的,使用一個開源的郵件組件,除了重構的時候誤用了多線程,查收郵件并解析某些郵件內容的時候還發現死活都是亂碼,然后分析郵件組件源碼才知道,這個郵件類庫使用UTF-8格式的編碼,而我們查收的用戶郵件很多都不是UTF-8格式的,而且還有一些多語言格式的郵件,比如俄語和日語,使用UTF-8解析也會碰到亂碼問題。
一個很簡單的函數,按照輸入長度截取字符串,一個漢字算兩個字符。 使用這個函數的時候,開發和測試環境都是通過的,但是生產環境發現截取的字符串就是不正確。最后發現這個函數的內部通過System.Text.Encoding.Default.GetByteCount(str)的方式獲取單字節字符長度。Encoding.Default靜態屬性是用于獲取操作系統的當前ANSI代碼頁的編碼,開發和測試沒有問題而生產有問題毫無疑問是因為操作系統的編碼設置不同造成的。后來該函數改造成強制使用GB2312格式的編碼,問題沒有重現。
雖然頁面交互的時候,很多系統之間的“切換”沒有顯式傳人編碼,但很多瀏覽器非常智能(實際可通過請求上下文得到具體編碼,比如asp.net中通過HttpContext.Current.Request.ContentEncoding.BodyName確定當前編碼方式),可以很輕松識別系統編碼或者使用默認編碼(),自動做出切換,所以有時候你不能很明顯發現問題。 但是對于需要編程實現一些業務的時候,不同編碼問題不能不考慮進去。比如你使用WebClient進行編程訪問頁面的時候,如果指定編碼不正確,亂碼了吧?猜對了。
還有一個簡單的例子,比如電商網站接入第三方支付: 在網站接入支付寶支付的時候,發現用戶從支付寶支付完成后,跳轉回到當前網站總是驗證簽名不通過。支付寶提供的接入文檔只是說,“在通知返回參數列表中,除去sign、sign_type兩個參數外,凡是通知返回回來的參數皆是要簽名的參數”。然后你按照NameValueCollection解析構造簽名,發現返回的簽名就是不正確,哪怕自己加上input_charset(這個參數支付寶沒有顯式返回,但是發起支付請求跳到支付寶的時候,我們使用的是UTF-8編碼格式)參與簽名也不行。 后來嘗試使用下面這種方式通過GB2312解碼構造簽名排序數組,竟然就通過了。
foreach (var item in collection.AllKeys) { if (item == AlipayField.sign) { continue; } if (item == AlipayField.sign_type) { continue; } //支付寶頁面同步返回的數據是用GBK編碼的 sortedList.Add(item, HttpUtility.UrlDecode(collection[item], Encoding.GetEncoding("GB2312"))); }AlipaySortedList
原來支付寶頁面同步返回的數據是用GBK編碼的,也就是說你提交請求給支付寶input_charset使用的是UTF-8編碼,支付寶頁面同步返回的是GBK編碼,而且支付寶不會把input_charset顯式返回。 對比一下財付通,財付通打破了這個潛規則,頁面同步通知的時候直接顯式返回input_charset,驗簽的時候按照財付通傳人的編碼方式進行解碼。
最后就是asp.net編碼和解碼,推薦使用系統自帶HttpUtility這個實用類,最好不要自己搞封裝,尤其是很可能無意識地引入了web請求上下文HttpConext.Current的編碼方法,說不定那天就會碰到問題。
參考:http://www.49028c.com/xiaomia/archive/2010/11/28/1890072.html
新聞熱點
疑難解答