寫在前面
當使用 GET 方式進行網路通訊時,引數會作為 URL 的一部分,此時需要對 URL 進行 Percent-Encoding
,即把一些字元轉換成百分號形式,如空格用 %20
代替。
Encoding and Decoding
在 iOS 上,使用的處理方法,主要有以下2個:
Encoding
// 除保留字元外,其他的都變成 % 形式
- [NSString stringByAddingPercentEncodingWithAllowedCharacters:];
複製程式碼
Decoding
// 移除 % 形式
- [NSString stringByRemovingPercentEncoding];
複製程式碼
所以關鍵是保留哪些字元?
[NSURLCharacterSet URLHostAllowedCharacterSet]
先從 iOS SDK 中找起,在 NSURL.h 中找到了 URLHostAllowedCharacterSet
這個靜態方法。
本以為使用官方方法處理後,就搞定了。但使用它處理後,數字也變成了百分號編碼,這不符合當前專案的要求。
那麼 URLHostAllowedCharacterSet
到底代表了那些字元?
仔細找過其標頭檔案,並沒有發現能知道包括字元的介面,甚至找過私有介面,也沒有收穫。
唯一可疑的方法 - (NSData *) bitmapRepresentation;
,但得到的是一個 NSData 物件,也沒能從中得到想要的資訊。
最終通過搜尋,在這裡找到了答案:
URLFragmentAllowedCharacterSet "#%<>[\]^`{|}
URLHostAllowedCharacterSet "#%/<>?@\^`{|}
URLPasswordAllowedCharacterSet "#%/:<>?@[\]^`{|}
URLPathAllowedCharacterSet "#%;<>?[\]^`{|}
URLQueryAllowedCharacterSet "#%<>[\]^`{|}
URLUserAllowedCharacterSet "#%/:<>?@[\]^`
複製程式碼
但筆者還是好奇,這些字元是怎麼找出來的?
RFC 3986
既然 iOS SDK 走不通,那直接找到標準保留字元,不就可以了嗎? RFC 3986:
reserved = gen-delims / sub-delims gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
即 :/?#[]@!$&'()*+,;=
但使用這些字元後,仍然不能正常和服務端通訊。
最後,請教了服務端同事,到底應該以哪些字元為標準。
JavaScript encodeURI()
因為服務端使用了 JavaScript 的解碼方法,所以統一標準,也使用 js 的編碼方式,即 encodeURI()
。
從文件中可以找到:
該方法不會對 ASCII 字母和數字進行編碼,也不會對這些 ASCII 標點符號進行編碼: - _ . ! ~ * ' ( ) 。 該方法的目的是對 URI 進行完整的編碼,因此對以下在 URI 中具有特殊含義的 ASCII 標點符號,encodeURI() 函式是不會進行轉義的:;/?:@&=+$,#
整理後即保留 -_.!~*;/?:@&=+$,#
+ 數字 + 字母。
翻譯成對應 Objc 程式碼:
NSMutableCharacterSet *lastSet = [[NSMutableCharacterSet alloc] init];
[lastSet formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@"-_.!~*;/?:@&=+$,#"]];
[lastSet formUnionWithCharacterSet:[NSCharacterSet letterCharacterSet]];
[lastSet formUnionWithCharacterSet:[NSCharacterSet alphanumericCharacterSet]];
return [self stringByAddingPercentEncodingWithAllowedCharacters:lastSet];
複製程式碼
至此,解決問題。