最近因項目須要,需重寫網絡組件。在重寫及review項目組的網絡組件舊代碼時,發現對URL編碼有不嚴謹之處。當說到這種寫法實際上是有問題時,幾個同事都表示很是詫異並幾度辯駁。本人表示有點當心驚,在網上搜索時還真的不多有另外的寫法。在此以本身的一些理解和經驗,作一下URL編碼的普及,但願對你們有所幫助,有問題也請不吝賜教。 (參考RFC1738,3986,6874,7320)web
1、瞭解URL編碼以及編碼時機、運算過程瀏覽器
URI包括URL和URN,經常使用說法URL encode實際是遵循URI的相關文件。在URI的最初設計時,但願能經過書面轉錄,好比寫在餐巾紙上告訴另一人,所以URI的構成字符必須是可寫的ASCII字符。在這些可書寫的字符裏,因爲一些字符在不一樣操做系統的編碼有不一樣的解析,被包含在Unsafe characters(圖3)之中,要格外注意。最後,在URI的構成字符中,最安全的方案是正確使用Reserved Characters (圖1)和 Unreserved Characters (圖1)的並集。
安全
在對非法字符編碼到合法URI時,規定使用percent encode編碼,對非法字符的編碼結果爲三個字節(%+16進制字符*2)。然而如何生成percent編碼,沒有明確的指導規定,這也是大部分開發者拷貝舊代碼殊不知其因此然的緣由。服務器
percent encode從字面上語義明確的指出其使用%作編碼標識,URL encode的實質就是正確的使用percent encode.網絡
正確完成URL encode的關鍵問題在於:何時,對哪些內容,採用何種過濾原則,以及如何生成percent編碼?函數
在WWW最初時,作法是將字符流轉換成字節流,按照ASCII字符與字節一一對應可相互轉換,使用對應ASCII字符的整型值做爲%的後兩個16進制字符,構成percent編碼。後來出現了多種percent編碼生成方法,致使了URI的難以識別。編碼
現下作法,包括iOS使用percent相關的函數時,指定或系統默認的使用UTF8轉成字節流,每一個字節編成一個percent編碼,例如中文「網易」的URL編碼爲%e7%bd%91%e6%98%93,而其UTF8字節流爲e7 bd 91 e6 93,能夠看出其一一對應關係。url
那麼percent編碼是在對非法字符采用某種編碼(約定爲UTF8)轉成字節流後,逐字節加上%構成percent編碼。spa
因爲不一樣scheme或協議對URI格式有不一樣的要求,RFC關於對哪些內容編碼,採用何種過濾原則不作硬性規定。而將決定權延後到執行時由開發者根據須要決定。一般遵循如下原則:操作系統
1.不要對Unreserved Characters作percent encode編碼。
2.除了保留字符和非保留字符外的全部字符,必須使用percent encode進行編碼。
3.保留字符不用於URI分隔符,而是用於其它位置,好比query部分的value時,要對這時用到的保留字符作percent encode編碼。
4.當兩個URI的字符幾乎對等,區別只在於一個對某些字符用的原有字符,另外一個URI對這些字符作了percent encode時。絕大部分狀況下,這兩個URI應當被認爲是不一樣的兩個URI。所以,不該當對保留字在做爲保留字的使用場景時使用percent encode編碼。
圖1. 保留字和非保留字
圖2 不安全字符
2、iOS開發中URL encode的方法編寫
有三個函數或系統方法用於URL encode,CFURLCreateStringByAddingPercentEscapes(9.0廢棄),stringByAddingPercentEscapesUsingencode(9.0廢棄),
stringByAddingPercentencodeWithAllowedCharacters(系統推薦)。該系統推薦方法默認使用了UTF8編碼而後再根據咱們指定容許的字符集完成percent編碼。一般咱們在拼接GET請求URL時使用URL encode。
應用場景通常是傳入URLString和一個參數NSDictionary, 這時須要傳入方保證URLString是已正確編碼的,而後遍歷NSDictionary的key和value, 按需指定容許的字符集對key值和value作編碼。結果輸出爲
+ (NSURL *) createGETURLFromString:(NSString *)urlString params:(NSDictionary *)params { NSURL *parsedURL = [NSURL URLWithString:urlString]; NSString* queryPrefix = parsedURL.query ? @"&" : @"?"; NSMutableArray* pairs = [NSMutableArray array]; for (NSString* key in [params keyEnumerator]) { if (![[params objectForKey:key] isKindOfClass:[NSString class]]) { continue; } NSString *value = (NSString *)[params objectForKey:key]; NSCharacterSet *allowedCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"]; NSString *urlEncodingKey = [key stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; NSString *urlEncodingValue = [value stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; [pairs addObject:[NSString stringWithFormat:@"%@=%@", urlEncodingKey, urlEncodingValue]]; } NSString* query = [pairs componentsJoinedByString:@"&"]; return [NSURL URLWithString:[NSString stringWithFormat:@"%@%@%@", urlString, queryPrefix, query]]; }
有的iOS開發者拿到 GET請求URLString和參數字典後,先拼接參數,而後再對整個字符串作URL encode,形成不能區分某些字符是處於分割組件的做用,或者是做爲組件的content。這是千萬不可取的,可能發生以下問題:
1.若是沒有正確過濾,好比http://www.baidu.com作編碼後變成了http%3a%2f%2fwww.baidu.com%2findex.htm,將不能正常訪問。 也就是說,爲了支持對拼接後的字符串做URL encode, 必須對整個拼接後的字符串禁止對全部URI的保留字做編碼,好比&字符,這就形成了問題2和3。