********************************************************************
* 版權(quán)聲明
*
* 本文以Creative Commons的
* 本文首發(fā)于博客園, 此聲明為本文章中不可或缺的一部分。
* 作者網(wǎng)名: 浪子
* 作者EMAIL:dayichen (at)163.com
* 作者BLOG: Http://Www.Cnblogs.Com/Walkingboy
*
********************************************************************
encodeURI/decodeURI與UrlEncode/UrlDecode,噩夢在繼續(xù)
-Written by 浪子@cnblogs.com (07-04-11)
摘要:
關(guān)于encodeURI,標準似乎是這么定義的:
"如果有空格就用%20代替,如果有其它字符就用%ASCII代替,如果有漢字等四個字節(jié)的字符,就用兩個%ASCII來代替"
然而MS向來標新立異,繼encodeURI之URL中文參數(shù)問題之后,encodeURI的噩夢繼續(xù)襲來......
一、該死的空格
最近做兩個頁面的數(shù)據(jù)交換.由PageA發(fā)起Ajax請求到PageB,PageB從數(shù)據(jù)庫讀取數(shù)據(jù)返回給PageA,由于怕中間有特殊字符會導(dǎo)致js失敗,所以使用了UrlEncode進行URI編碼,再在客戶端進行decodeURI解碼.
結(jié)果發(fā)現(xiàn)空格無法被正確識別,UrlEncode將空格編碼為+,而decodeURI只識別20%表示的空格。初步判斷UrlEncode的編碼格式和encodeURI不一致,為了驗證這個看法,于是提取了鍵盤上的一些特殊符號進行編碼比對:
字符: ~ ! @ # $ % ^ & * ()_ + - = |
UrlEncode: %7e ! %40 %23 %24 %25 %5e %26 * ()_ %2b - %3d |
encodeURI: ~ ! @ # $ %25 %5E & * ()_ + - = |
字符: { } [ ] \ | ' ; : " / ? . , < > |
UrlEncode: %7b %7d %5b %5d %5c %7c ' %3b %3a %22 %2f %3f . %2c %3c %3e |
encodeURI: %7B %7D %5B %5D %5C %7C ' ; : %22 / ? . , %3C %3E |
這些比較可以看出,兩個的編碼確實存在比較大區(qū)別上,特別是對于特殊字符的處理上面.
由此相當,在encodeURI之URL中文參數(shù)問題中自己所認為了,asp.net對form的action進行了2次編碼的判斷應(yīng)該是錯的,其實并沒有進行2次編碼,只是asp.net在接受到encodeURI編碼的action之后,利用UrlDecode進行解碼,然后再次用UrlEncode進行編碼寫入Html中,由于編碼格式不一致,所以Postback之后的URI,js就無法使用decodeURI進行正確解碼.
由此可以知道,如果你用encodeURI編碼的字符串,是可以通過UrlDecode解碼出來的,也就是說UrlDecode可以識別encodeURI(js)和UrlEncode(c#)兩個編碼格式.可以想到,MS在設(shè)計這個類庫的時候,已經(jīng)考慮到了會接受到encodeURI的編碼,按常理來想的話,既然考慮到了解碼,自然會考慮到編碼,也即UrlEncode應(yīng)該提供可以編碼成decodeURI可以解碼的格式.可別的是,我一直無法找到這個方式.不知道是設(shè)計者給我們開的一個小玩笑,還是留下點瑕疵好讓我們?nèi)计鹁幊痰募で?不至于對千篇一律的Code工作感到厭倦,殘念......
二、讓SP來得更猛烈些吧
因為存在這個編碼的不一致性,導(dǎo)致如果你的程序需要做比較多的Server-Client數(shù)據(jù)溝通的話,只能通過其他途徑(json,xml等非URI),即使只是一個簡單的字符串,你也需要增加許多額外的數(shù)據(jù)以滿足你的格式.
一如MS的很多軟件一樣,SP滿天飛,看來我也只好自己進行SP了.
分析下編碼中差異,基本都集中在特殊字符的處理上,對于中文的處理貌似一致的(目前還沒有測試出差異).于是定下了"利用encodeURI/decodeURI處理中文字符,其他的進行手工處理"的方案,修改了下之前的js代碼:
KINN.Util.EncodeURI = function(unzipStr,isCusEncode){ |
if(isCusEncode){ |
var zipArray = new Array(); |
var zipstr = ""; |
var lens = new Array(); |
for(var i=0;i<unzipStr.length;i++){ |
var ac = unzipStr.charCodeAt(i); |
zipstr += ac; |
lens = lens.concat(ac.toString().length); |
} |
zipArray = zipArray.concat(zipstr); |
zipArray = zipArray.concat(lens.join("O")); |
return zipArray.join("N"); |
}else{ |
//return encodeURI(unzipStr); |
var zipstr=""; |
var strSpecial="!\"#$%&'()*+,/:;<=>?[]^`{|}~%"; |
var tt= ""; |
for(var i=0;i<unzipStr.length;i++){ |
var chr = unzipStr.charAt(i); |
var c=KINN.Util.StringToAscii(chr); |
tt += chr+":"+c+"n"; |
if(parseInt("0x"+c) > 0x7f){ |
zipstr+=encodeURI(unzipStr.substr(i,1)); |
}else{ |
if(chr==" ") |
zipstr+="+"; |
else if(strSpecial.indexOf(chr)!=-1) |
zipstr+="%"+c.toString(16); |
else |
zipstr+=chr; |
} |
} |
return zipstr; |
} |
} |
KINN.Util.DecodeURI = function(zipStr,isCusEncode){ |
if(isCusEncode){ |
var zipArray = zipStr.split("N"); |
var zipSrcStr = zipArray[0]; |
var zipLens; |
if(zipArray[1]){ |
zipLens = zipArray[1].split("O"); |
}else{ |
zipLens.length = 0; |
} |
var uzipStr = ""; |
for(var j=0;j<zipLens.length;j++){ |
var charLen = parseInt(zipLens[j]); |
uzipStr+= String.fromCharCode(zipSrcStr.substr(0,charLen)); |
zipSrcStr = zipSrcStr.slice(charLen,zipSrcStr.length); |
} |
return uzipStr; |
}else{ |
//return decodeURI(zipStr); |
var uzipStr=""; |
for(var i=0;i<zipStr.length;i++){ |
var chr = zipStr.charAt(i); |
if(chr == "+"){ |
uzipStr+=" "; |
}else if(chr=="%"){ |
var asc = zipStr.substring(i+1,i+3); |
if(parseInt("0x"+asc)>0x7f){ |
uzipStr+=decodeURI("%"+asc.toString()+zipStr.substring(i+3,i+9).toString()); ; |
i+=8; |
}else{ |
uzipStr+=KINN.Util.AsciiToString(parseInt("0x"+asc)); |
i+=2; |
} |
}else{ |
uzipStr+= chr; |
} |
} |
return uzipStr; |
} |
} |
KINN.Util.StringToAscii = function(str){ |
return str.charCodeAt(0).toString(16); |
} |
KINN.Util.AsciiToString = function(asccode){ |
return String.fromCharCode(asccode); |
} |