1. 程式人生 > >okhttp addHeader 字串中含有中文引起的Crash

okhttp addHeader 字串中含有中文引起的Crash

java.lang.IllegalArgumentException

Unexpected char 0x950b at 35 in User-Agent value: Mozilla/5.0 (Linux; Android 4.4.4; 鋒尚MAX Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Mobile Safari/537.36

異常最終是拋在了Headers中的checkNameAndValue,OKHttp 設定請求頭時,是不支援換行和中文字元的,在新增頭時會做Header的Key和Value的檢查,如果發現非法字元則丟擲上述異常。

private void checkNameAndValue(String name, String value) {
  if (name == null) throw new IllegalArgumentException("name == null");
  if (name.isEmpty()) throw new IllegalArgumentException("name is empty");
  for (int i = 0, length = name.length(); i < length; i++) {
    char c = name.charAt(i);
if (c <= '\u001f' || c >= '\u007f') { throw new IllegalArgumentException(String.format( "Unexpected char %#04x at %d in header name: %s", (int) c, i, name)); } } if (value == null) throw new IllegalArgumentException("value == null"); for (int i = 0, length = value.length();
i < length; i++) { char c = value.charAt(i); if (c <= '\u001f' || c >= '\u007f') { throw new IllegalArgumentException(String.format( "Unexpected char %#04x at %d in %s value: %s", (int) c, i, name, value)); } } }

因為服務端要統計一些資訊,所以約定每個請求都需要上傳UA。新增UA是通過實現NetworkInterceptor 網路攔截器實現每個請求都帶有UA的,國產手機廠商會把手機型號加到UA裡,也許是為了統計吧,之前的AsyncHttp沒有這個問題,測試裝置中也並未有ua帶中文的測試機,這個問題被忽略掉,吐血…… 第一個異常是因為用了全形的空格,全形的空格Unicode碼是\u3000。

解決方案:

因為UA服務端需要做統計,所以不能直接使用Base64編碼,服務端沒有解碼邏輯需要對獲取到的UA做合法性校驗,遇到不符合條件的字元就過濾掉,過濾掉這些字元對統計應該不會有什麼影響。

private static String getValidUA(String userAgent){
    StringBuffer sb = new StringBuffer();
    for (int i = 0, length = userAgent.length(); i < length; i++) {
        char c = userAgent.charAt(i);
        if (!(c <= '\u001f' || c >= '\u007f')) {
           sb.append(c);}
    }
    return sb.toString();
}
在網上看到另外兩種方案,也是可行的。

法1:檢測為不合法字元,就轉為unicode 編碼,OkHttp 中的checkNameAndValue去遍歷每個字元就不會為非法了

private static String getValidUA(String userAgent){
    StringBuffer sb = new StringBuffer();
    for (int i = 0, length = userAgent.length(); i < length; i++) {
        char c = userAgent.charAt(i);
        if (c <= '\u001f' || c >= '\u007f') {
            sb.append(String.format("\\u%04x", (int) c));
} else {
            sb.append(c);
}
    }
    return sb.toString();
}

法2:如果發現非法字元,採用UrlEncode對其進行編碼

private String getValidUA(String userAgent){
    if(TextUtils.isEmpty(userAgent)){
        return "android";
}
    String validUA = "android";
String uaWithoutLine = userAgent.replace("\n", "");
    for (int i = 0, length = uaWithoutLine.length(); i < length; i++){
        char c = userAgent.charAt(i);
        if (c <= '\u001f' || c >= '\u007f') {
            try {
                validUA = URLEncoder.encode(uaWithoutLine, "UTF-8");
} catch (UnsupportedEncodingException e) {
                e.printStackTrace();
}
            return validUA;
}
    }
    return uaWithoutLine;
}