URL編碼(URL encoding)也稱為百分號(hào)編碼(Percent-encoding), 是特定上下文統(tǒng)一資源定位符(URI)
的編碼機(jī)制. 實(shí)際上也使用與統(tǒng)一資源標(biāo)志符(URI)的編碼.
對(duì)于URI, 具體的結(jié)構(gòu)如下:
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/ \________/\_________/ \__/
| | | | |
scheme authority path query fragment
我們能夠看到:/?#[]@
是用來分隔URI的不同的組件的.
混亂的URL編碼
具體參考: 阮一峰: 關(guān)于URL編碼.
URI的字符類型
URI所允許的字符分成保留
與未保留
. 保留
字符是那些具有特殊含義的字符. 例如, /
字符用于URL不同部分的分節(jié)符(https://www.baidu.com/news
). 未保留
字符沒有這些特殊含義. 百分號(hào)編碼把保留
字符表示為特殊字符序列, 根據(jù)URI的版本的不同略有變化, 下面是 RFC 3986
的保留
字符, 與未保留
字符:
保留:
! * ' ( ) ; : @ & = + $ , / ? # [ ]
未保留:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 - _ . ~
除此之外, URI中的其它字符必須用百分號(hào)編碼.
URI的保留字符的百分號(hào)編碼
前面我們知道, URI的保留字符有特殊含義(reserved purpose
), 并且URI中必須使用該字符用于其他目的, 那么該字符必須百分號(hào)編碼. 這里所說的其他目的, 我們可以這樣理解, 在URL中?key1=val1&key2=val2
中的val1
中如果有保留字符&或者*
,那么這里保留
字符用于其他目的, 需要編碼.
百分號(hào)編碼一個(gè)保留字符, 首先需要把該字符的ASCII的值表示為兩個(gè)16進(jìn)制的數(shù)字, 然后在其前面放置轉(zhuǎn)義字符("%"), 置入U(xiǎn)RI中的相應(yīng)位置. (對(duì)于非ASCII字符, 需要轉(zhuǎn)換為UTF-8字節(jié)序, 然后每個(gè)字節(jié)按照上述方式表示.)
! # $ & ' ( ) * + , / : ; = ? @ [ ]
%21 %23 %24 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D
在特定上下文中沒有特殊含義的保留字符也可以被百分號(hào)編碼, 在語(yǔ)義上與不百分號(hào)編碼的該字符沒有差別(這個(gè)特點(diǎn)非常重要, 如果我們不知道該字符是否需要被百分號(hào)編碼, 那么最好用百分號(hào)編碼一下).
在URI的查詢
部分(?字符后的部分
)中, 例如/
仍然是保留字符但是沒有特殊含義, 除非一個(gè)特定的URI有其它規(guī)定. 該/
字符在沒有特殊含義時(shí)不需要百分號(hào)編碼.例如https://www.baidu.com/news?name=p/p&age=13
, 其中name=p/p
中的/
是保留字符
但是沒有特殊含義, 在實(shí)際使用中可以不用給它進(jìn)行百分號(hào)編碼.
如果保留字符具有特殊含義, 那么該保留字符用百分號(hào)編碼的URI與該保留字符僅用其自身表示的URI具有不同的語(yǔ)義.
URI中百分號(hào)編碼未保留字符
未保留字符不需要百分號(hào)編碼.
兩個(gè)URI的差別如果僅在于未保留字符
是用百分號(hào)編碼還是字符本身表示, 那么這兩個(gè)URI具有等價(jià)意義. 雖然是這樣規(guī)定, 但是很多瀏覽器沒有這樣去設(shè)定.因此實(shí)際開發(fā)中, 我們建議, 盡量不要將未保留字符
進(jìn)行百分號(hào)編碼, 防止不同的實(shí)現(xiàn)導(dǎo)致不同的結(jié)果.
其他不安全字符也需要百分號(hào)編碼
這些字符, 當(dāng)被直接放在URL中的時(shí)候, 能會(huì)引起解析程序的歧義救欧。這些字符被視為不安全字符,原因有很多:
-
空格
: URL在傳輸?shù)倪^程,或者用戶在排版的過程,或者文本處理程序在處理URL的過程,都有可能引入無(wú)關(guān)緊要的空格刁岸,或者將那些有意義的空格給去掉独郎。 -
引號(hào)以及<>
: 引號(hào)和尖括號(hào)通常用于在普通文本中起到分隔Url的作用 -
#
: 通常用于表示書簽或者錨點(diǎn) -
%
: 百分號(hào)本身用作對(duì)不安全字符進(jìn)行編碼時(shí)使用的特殊字符,因此本身需要編碼 - " {}|^[]`~ ": 某一些網(wǎng)關(guān)或者傳輸代理會(huì)篡改這些字符.
因此這些不安全的字符最好也進(jìn)行百分號(hào)編碼.
URI中百分號(hào)編碼的非標(biāo)準(zhǔn)實(shí)現(xiàn)
有一些不符合標(biāo)準(zhǔn)的把Unicode字符在URI中表示為: %uxxxx
, 其中xxxx
是用4個(gè)十六進(jìn)制數(shù)字表示的Unicode的碼位值壳猜。
任何RFC都沒有這樣的字符表示方法勾徽,并且已經(jīng)被W3C拒絕。第三版的ECMA-262
仍然包含函數(shù)escape(string)
使用這種語(yǔ)法, 但也有函數(shù)encodeURI(uri)
轉(zhuǎn)換字符到UTF-8字節(jié)序列并用百分號(hào)編碼每個(gè)字節(jié)统扳。
這里就涉及到
JS
的幾個(gè)百分號(hào)編碼函數(shù), 建議使用encodeURI(uri)
iOS中百分號(hào)編碼問題
在前序知識(shí)鋪墊以后. iOS里面如何處理百分號(hào)編碼的問題呢?
HTTP協(xié)議里面在URL中傳遞參數(shù),是在?
后面使用key=value
這種鍵值對(duì)方式, 如果有多個(gè)參數(shù)傳遞, 就需要用&
進(jìn)行分割, 例如?key1=val1&key2=val2&key3=val3
, 當(dāng)服務(wù)器收到請(qǐng)求以后, 會(huì)用&
分割出每個(gè)key=value
參數(shù), 然后用=
分割出具體的鍵值.
現(xiàn)在如果在我們的參數(shù)key-value
中就有=
或者&
怎么辦? 這樣后臺(tái)在解析參數(shù)的時(shí)候, 就會(huì)產(chǎn)生歧義. 因此解決方法就是對(duì)參數(shù)進(jìn)行百分號(hào)編碼!!!!
iOS 中,我們?cè)谡?qǐng)求的中經(jīng)常與百分號(hào)編碼相關(guān)的方法 -- stringByAddingPercentEscapesUsingEncoding:
Summary
Returns a representation of the receiver using a given encoding to determine the percent escapes necessary to convert the receiver into a legal URL string.
Declaration
- (NSString *)stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)enc;
Discussion
It may be difficult to use this function to "clean up" unescaped or partially escaped URL strings where sequences are unpredictable. See CFURLCreateStringByAddingPercentEscapes for more information.
Parameters
encoding
The encoding to use for the returned string. If you are uncertain of the correct encoding you should use NSUTF8StringEncoding.
Returns
A representation of the receiver using encoding to determine the percent escapes necessary to convert the receiver into a legal URL string. Returns nil if encoding cannot encode a particular character.
Open in Developer Documentation
當(dāng)URL中有漢字時(shí)候, 用上面的方法, 會(huì)將漢字轉(zhuǎn)化成 unicode 編碼的結(jié)果, 但是對(duì)于復(fù)雜場(chǎng)景這個(gè)方法并不能滿足需求, 例如&
符號(hào):
NSString *queryWord = @"漢字&ss";
NSString *urlString = [NSString stringWithFormat:@"https://www.baidu.com/s?ie=UTF-8&wd=%@", queryWord];
NSString *escapedString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"%@", escapedString); // https://www.baidu.com/s?ie=UTF-8&wd=%E6%B1%89%E5%AD%97&ss
這個(gè)實(shí)例在開發(fā)中很常見(我們項(xiàng)目中是將某個(gè)人的昵稱當(dāng)做參數(shù)傳遞給后臺(tái)), 后臺(tái)在收到這種被轉(zhuǎn)義以后的URL取得的參數(shù)如下:
["ie": "UTF-8", "wd" : "漢字", "ss": nil]
即使我們做如下處理, 在請(qǐng)求前將每個(gè)參數(shù)都轉(zhuǎn)義, 再使用&
拼接參數(shù)也無(wú)效:
NSString *queryWord = @"漢字&ss";
NSString *escapedQueryWord = [queryWord stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *urlString = [NSString stringWithFormat:@"https://www.baidu.com/s?ie=UTF-8&wd=%@", escapedQueryWord];
NSLog(@"%@", urlString); // https://www.baidu.com/s?ie=UTF-8&wd=%E6%B1%89%E5%AD%97&ss
這是因?yàn)?code>stringByAddingPercentEscapesUsingEncoding方法并不會(huì)對(duì)&
字符進(jìn)行百分號(hào)編碼!!!!
iOS中正確的使用百分號(hào)編碼
如果要想自己控制哪些內(nèi)容被編碼, 哪些內(nèi)容不會(huì)被編碼, iOS提供了另外一個(gè)方法 -- stringByAddingPercentEncodingWithAllowedCharacters:
.
這個(gè)方法會(huì)對(duì)字符串進(jìn)行更徹底的轉(zhuǎn)義捂蕴,但是需要傳遞一個(gè)參數(shù): 這個(gè)參數(shù)是一個(gè)字符集,表示: 在進(jìn)行轉(zhuǎn)義過程中闪幽,不會(huì)對(duì)這個(gè)字符集中包含的字符進(jìn)行轉(zhuǎn)義, 而保持原樣保留下來啥辨。
NSString *queryWord = @"漢字&ss";
NSString *escapedQueryWord = [queryWord stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet letterCharacterSet]];
NSLog(@"%@", escapedQueryWord); // %E6%B1%89%E5%AD%97%26ss
NSString *urlString = [NSString stringWithFormat:@"https://www.baidu.com/s?ie=UTF-8&wd=%@", escapedQueryWord];
NSLog(@"%@", urlString); // https://www.baidu.com/s?ie=UTF-8&wd=%E6%B1%89%E5%AD%97%26ss
在上面的例子中傳遞參數(shù)[NSCharacterSet letterCharacterSet]
來保證字母
不被轉(zhuǎn)義。所以被轉(zhuǎn)義之后的參數(shù)值是:%E6%B1%89%E5%AD%97%26ss
盯腌,這樣&
就能夠正確被百分號(hào)編碼.
但是如果實(shí)際場(chǎng)景中, 可能出現(xiàn)如下情況:
https://www.baidu.com/s?person[contact]=13801001234&person[address]=北京&habit[]=游泳&habit[]=騎行
此時(shí), 需要自己構(gòu)建 AllowedCharacters
, 因?yàn)槠渲械?code>[和]
是不需要轉(zhuǎn)意的.
NSMutableCharacterSet *mutableCharSet = [[NSMutableCharacterSet alloc] init];
[mutableCharSet addCharactersInString:@"[]"]; // 允許'['和']'不被轉(zhuǎn)義
NSCharacterSet *charSet = mutableCharSet.copy;
NSMutableString *mutableString = [NSMutableString string];
for (unit in queryString) {
NSString *escapedField = [unit.field stringByAddingPercentEncodingWithAllowedCharacters:charSet];
NSString *escapedValue = [unit.value stringByAddingPercentEncodingWithAllowedCharacters:charSet];
[mutableString addFormat:@"%@=%@", escapedField, escapedValue];
}
準(zhǔn)確說, 步驟如下:
- 構(gòu)建
AllowedCharacters
的NSCharacterSet
- 針對(duì)參數(shù)的k-v值, 進(jìn)行遍歷, 將針對(duì)
key
和value
分別調(diào)用stringByAddingPercentEncodingWithAllowedCharacters
進(jìn)行百分號(hào)編碼. - 用
@"?%@=%@&%@=%@"
進(jìn)行kv參數(shù)拼接, 和不同參數(shù)的拼接.
AFNetworking中對(duì)百分號(hào)編碼的處理
對(duì)這種特定的字符進(jìn)行百分號(hào)編碼已經(jīng)能夠滿足基本需求, 但是如果我們傳遞的參數(shù)非常復(fù)雜, 我們應(yīng)該如何處理呢??
我們可以直接使用AFNetworking中的代碼, 實(shí)例如下:
NSDictionary *params = @{@"name": @"p&p",
@"nick_name": @"p&p[]@= =!",
@"father name": @"~!@#$%^&*(){}"
};
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
NSMutableURLRequest *request = [serializer requestWithMethod:@"GET" URLString:@"https://www.baidu.com" parameters:params error:nil];
NSString *urlString = request.URL.absoluteString;
NSLog(@"%@", urlString);
//https://www.baidu.com?father%20name=~%21%40%23%24%25%5E%26%2A%28%29%7B%7D&name=p%26p&nick_name=p%26p%5B%5D%40%3D%20%3D%21
具體的處理方式, 建議參考AFNetworking的源碼, 或者參考文章iOS. PercentEscape是錯(cuò)用的URLEncode溉知,看看AFN和Facebook吧.
AFNetworking源碼以及編碼過程
關(guān)于AFNetworking中是如何做的:
FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary);
FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value);
FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string);
@interface AFQueryStringPair : NSObject
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;
- (instancetype)initWithField:(id)field value:(id)value;
- (NSString *)URLEncodedStringValue;
@end
@implementation AFQueryStringPair
- (instancetype)initWithField:(id)field value:(id)value {
self = [super init];
if (!self) {
return nil;
}
self.field = field;
self.value = value;
return self;
}
- (NSString *)URLEncodedStringValue {
if (!self.value || [self.value isEqual:[NSNull null]]) {
return AFPercentEscapedStringFromString([self.field description]);
} else {
return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
}
}
/**
傳入 Dict -> 返回對(duì)應(yīng)的查詢的參數(shù)String
@param parameters Dict內(nèi)部是 key-value
@return 查詢的參數(shù)String
*/
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
// 生成一組 AFQueryStringPair 數(shù)組
NSArray<AFQueryStringPair *> *pairs = AFQueryStringPairsFromDictionary(parameters);
// 遍歷數(shù)組, 將每個(gè) AFQueryStringPair 生成 "key=value", 并將結(jié)果String加入到結(jié)果數(shù)組
for (AFQueryStringPair *pair in pairs) {
// 將封裝的 StringPair 進(jìn)行 URLEncode 核心代碼
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
// 將結(jié)果數(shù)組中每個(gè)字符通過 "&" 字符鏈接, 輸出 Query 結(jié)果
return [mutablePairs componentsJoinedByString:@"&"];
}
/**
將dict 轉(zhuǎn)化成 NSArray<AFQueryStringPair *>
*/
NSArray<AFQueryStringPair *> * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
/**
key - value 核心方法
@param key key
@param value value -- 可能是常用的集合類 -- NSDictionary, NSArray, NSSet,
以及非集合類 -- 普通的 key - value
@return 返回NSArray<AFQueryStringPair *> *
*/
NSArray<AFQueryStringPair *> * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
//1. key-value 會(huì)重新排序 -- 升序進(jìn)行排序
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
//2. 根據(jù)當(dāng)前value內(nèi)容, 分辨進(jìn)行不同的處理. 實(shí)際場(chǎng)景中, 我們最常用的是 Dict - 內(nèi)部是key-value
// 場(chǎng)景上來說
/*
2.1 value -> NSDictionary
NSDictionary *dict = @{@"phone": @{@"mobile": @"xx", @"home": @"xx"};
-> 會(huì)進(jìn)入第一個(gè)分支 - Dict分支
phone[mobile]=xx&phone[home]=xx
*/
if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = value;
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
// 先將dictionarys 內(nèi)容排序
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
// 遞歸調(diào)用.
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
/*
2.2 value -> NSArray
NSDictionary *dict = @{"members": @[@"pp", @"brownfeng"]};
-> Array分支
members[]=pp&members[]=brownfeng
*/
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
/*
2.3 value -> NSSet
NSDictionary *dict = @{@"counts": [NSSet setWithObjects:@"1", @"2", nil]};
-> NSSet分支
counts=1&counts=2
*/
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}
} else {
/*
2.4 value -> NSString
普通的 key-value類型. 直接生成 AFQueryStringPair
NSDictionary *dict = @{@"name": @"pp"};
-> 其他分支
name=p
*/
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}
/**
百分號(hào)編碼的核心代碼!!!!!
Returns a percent-escaped string following RFC 3986 for a query string key or value.
RFC 3986 states that the following characters are "reserved" characters.
- General Delimiters: ":", "#", "[", "]", "@", "?", "/" -> 常見的分隔符
- Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" -> 其他分隔符
In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
should be percent-escaped in the query string.
- parameter string: The string to be percent-escaped.
- returns: The percent-escaped string.
上面注釋寫的很清楚:
"?"和"/"兩個(gè)符號(hào)在query必須進(jìn)行百分號(hào)編碼, 因?yàn)閝uery部分不允許包含URL!!!!
*/
NSString * AFPercentEscapedStringFromString(NSString *string) {
// 需要被百分號(hào)編碼 - @":#[]@"
static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
// 需要被百分號(hào)編碼 - @"!$&'()*+,;="
static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
// "?", "/" - 沒有被百分號(hào)編碼!!!!
// 1. 創(chuàng)建字符集 - URL Query 部分允許的字符集
NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
// 2. 從允許字符集中刪掉不允許的字符集, 因此編碼時(shí)候,
[allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];
// 解決iOS7,8中可能導(dǎo)致的crash問題
// FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028
// return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
static NSUInteger const batchSize = 50;
NSUInteger index = 0;
NSMutableString *escaped = @"".mutableCopy;
while (index < string.length) {
NSUInteger length = MIN(string.length - index, batchSize);
NSRange range = NSMakeRange(index, length);
// To avoid breaking up character sequences such as ????????
range = [string rangeOfComposedCharacterSequencesForRange:range];
NSString *substring = [string substringWithRange:range];
NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
[escaped appendString:encoded];
index += range.length;
}
return escaped;
}
因此如果比較復(fù)雜的query內(nèi)容比如如下:
{
NSDictionary *params = @{
@"name": @"小A", // 標(biāo)準(zhǔn) key-value, 漢字需要被編碼
@"phone": @{@"mobile": @"xx", @"home": @"xx"}, // key - Dict
@"families": @[@"father", @"mother"], // key - Array
@"nums": [NSSet setWithObjects:@"1", @"2", nil], // key - set
@"does_not_include": @"/?", // 不會(huì)被編碼 (注意: OC中的"\\"才能表示"\")
@"space": @" ", //需要被編碼 (空格)
@"GeneralDelimitersToEncode": @":#[]@", // 需要完全被編碼
@"SubDelimitersToEncode": @"!$&'()*+,;=", // 需要完全被編碼
};
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
NSMutableURLRequest *request = [serializer requestWithMethod:@"GET" URLString:@"https://www.baidu.com" parameters:params error:nil];
NSString *urlString = request.URL.absoluteString;
NSLog(@"%@", urlString);
/*
https://www.baidu.com?GeneralDelimitersToEncode=%3A%23%5B%5D%40&SubDelimitersToEncode=%21%24%26%27%28%29%2A%2B%2C%3B%3D&does_not_include=/?&families%5B%5D=father&families%5B%5D=mother&name=%E5%B0%8FA&nums=1&nums=2&phone%5Bhome%5D=xx&phone%5Bmobile%5D=xx&space=%20
https://www.baidu.com?GeneralDelimitersToEncode=:#[]@&SubDelimitersToEncode=!$&'()*+,;=&does_not_include=/?&families[]=father&families[]=mother&name=小A&nums=1&nums=2&phone[home]=xx&phone[mobile]=xx&space=
*/
}
AFNetworking中的參數(shù)解析過程如下:
第一塊, key-value的模式
@{
@"name": @"小A",
@"phone": @{@"mobile": @"xx", @"home": @"xx"},
@"families": @[@"father", @"mother"],
@"nums": [NSSet setWithObjects:@"1", @"2", nil],
};
->
@[
field: @"name", value: @"小A",
field: @"phone[mobile]", value: @"xx",
field: @"phone[home]", value: @"xx",
field: @"families[]", value: @"father",
field: @"families[]", value: @"mother",
field: @"nums", value: @"1",
field: @"nums", value: @"2",
]
->
name=%E5%B0%8FA&phone[mobile]=xx&phone[home]=xx&families[]=father&families[]=mother&nums=1&num=2
第二部分: 哪些內(nèi)容需要編碼
@{
@"does_not_include": @"/?",
@"space": @" ",
@"GeneralDelimitersToEncode": @":#[]@",
@"SubDelimitersToEncode": @"!$&'()*+,;=",
};
->
@[
field: @"does_not_include", value: @"/?",
field: @"space", value: @" ",
field: @"GeneralDelimitersToEncode", value: @":#[]@",
field: @"SubDelimitersToEncode", value: @"!$&'()*+,;=",
]
->
https://www.baidu.com?GeneralDelimitersToEncode=%3A%23%5B%5D%40&SubDelimitersToEncode=%21%24%26%27%28%29%2A%2B%2C%3B%3D&does_not_include=/?&space=%20
-> URL Decode以后
https://www.baidu.com?GeneralDelimitersToEncode=:#[]@&SubDelimitersToEncode=!$&'()*+,;=&does_not_include=/?&space=