介绍
在发送请求之前需要配置request的各项参数,比如请求内容的编码,AFNetWorking中使用AFQueryStringPair类用来对网络请求数据进行编码(百分号编码)遵循rfc3986,
AFQueryStringPair类通过函数NSString * AFPercentEscapedStringFromString(NSString *string){}实现编码。
预备知识:
1:URL理编码:
URL理编码时(使用%编码)默认根据ASCII国际标准(0-127范围)根据分析iOS CFURL源码(https://opensource.apple.com/source/CF/CF-855.14/)可以看到static const unsigned char sURLValidCharacters[128] 变量,存放了系统允许的字符集。
2:对于URL字符集的支持,根据系统函数
[NSCharacterSet URLUserAllowedCharacterSet]
[NSCharacterSet URLHostAllowedCharacterSet]
[NSCharacterSet URLPasswordAllowedCharacterSet]
[NSCharacterSet URLPathAllowedCharacterSet]
[NSCharacterSet URLQueryAllowedCharacterSet]
[NSCharacterSet URLFragmentAllowedCharacterSet]
函数获得,获取出来的是一个NSCharacterSet 对象,系统未开放API显示字符集,我通过循环遍历0-127之间的字符是否存在当前的字符集对象中,整理出以下系统对URL 不同segment支持的字符集,拿URLQueryAllowedCharacterSet举例:
NSMutableCharacterSet * URLQueryAllowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
NSMutableArray *URLQueryAllowedCharacters = [NSMutableArray arrayWithCapacity:0];
for (int i = 0; i < 128; i++) {
NSString *string =[NSString stringWithFormat:@"%c",i];
if ([string rangeOfCharacterFromSet:URLQueryAllowedCharacterSet].location != NSNotFound) {
[URLQueryAllowedCharacters addObject:string];
};
}
NSLog(@"allowedCharacters = %@\n\n",[URLQueryAllowedCharacters componentsJoinedByString:@" "]);
其它的可以效仿上面的方式输出,最终所有的字符集输出如下:
2016-11-24 11:01:55.396 test2[1775:50836] -------------URLUserAllowedCharacterSet-------------
2016-11-24 11:01:55.397 test2[1775:50836] allowedCharacters = ! $ & ' ( ) * + , - . 0 1 2 3 4 5 6 7 8 9 ; = 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 ~
2016-11-24 11:01:55.398 test2[1775:50836] -------------URLPasswordAllowedCharacterSet-------------
2016-11-24 11:01:55.399 test2[1775:50836] allowedCharacters = ! $ & ' ( ) * + , - . 0 1 2 3 4 5 6 7 8 9 ; = 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 ~
2016-11-24 11:01:55.399 test2[1775:50836] -------------URLHostAllowedCharacterSet-------------
2016-11-24 11:01:55.400 test2[1775:50836] allowedCharacters = ! $ & ' ( ) * + , - . 0 1 2 3 4 5 6 7 8 9 : ; = 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 ~
2016-11-24 11:01:55.400 test2[1775:50836] -------------URLPathAllowedCharacterSet-------------
2016-11-24 11:01:55.401 test2[1775:50836] allowedCharacters = ! $ & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : = @ 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 ~
2016-11-24 11:01:55.401 test2[1775:50836] -------------URLQueryAllowedCharacterSet-------------
2016-11-24 11:01:55.402 test2[1775:50836] allowedCharacters = ! $ & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; = ? @ 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 ~
2016-11-24 11:01:55.403 test2[1775:50836] -------------URLFragmentAllowedCharacterSet-------------
2016-11-24 11:01:55.403 test2[1775:50836] allowedCharacters = ! $ & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; = ? @ 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 ~
AFNetWorking对URL编码实现
1:编码实现(此实现可以单独作为一个扩展实现使用到自己的项目中)
NSString * AFPercentEscapedStringFromString(NSString *string) {
① static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@";
② static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
③ NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
④ [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];
⑤ // 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;
}
解释:
①和②根据rfc3986创建在URI中query部分进行百分号编码的字符
③获取iOS系统针对URI中query部分允许出现的字符集
④根据rfc的规范去除①和②中出现的字符,得到最终的在query部分可以出现的字符集(不被百分号编码)
⑤介绍了一个关于系统直接编码产生的bug,因为在query中可能会出现emoji等特殊字符,这些字符的在编码过程中出现问题,具体的介绍往后看⑥-⑦的介绍
⑥-⑦:因为iOS系统的字符串编码默认使用utf16,中文字符的长度是1,实际上是占用两个字节,对于emoji (表情符号)等特殊字符,这些字符的长度是4,在对其编码的时候需要按位进行,如果只是简单的取出每一位的字符,就会产生断字。因此需要使用字符串的rangeOfComposedCharacterSequencesForRange: 方法达到整取的效果,比如某个字符串中有一个英文字符和3个表情符号,长度(length)应该是13,这时候取长度10,就只能取一个英文字符和连个表情字符,达到整取的效果,不会产生断字,影响到编码,此处代码中有个 batchSize 设置为50,是个默认值,这个地方你也可以这是10,或者100。主要是为了而快速的取值,算是一个策略值吧