转载

如何对URL字符串进行百分号编码

如何对URL字符串进行百分号编码

原文: How to percent encode a URL String

作者: @kharrison

译者:CocoaChina-- 飘

在和web服务进行交互时,我们经常需要对URL中的特定字符和传输的表单数据进行 百分号编码 。例如,’&’在百分号编码时会变成’%26’。搞清楚 URL中哪部分的哪些字符应该进行百分号编码了并不是件易事。最好的资料好像是 RFC 3986 和 W3C HTML5 。出于兴趣和教育目的,我创建了swift的String的扩展(和作为对比的Objective-C的分类)。

RFC3986 编码查询字符串

在 RFC3986 的第2.3节列出了你不需要百分号编码的字符,因为它们在URL中没有特殊的含义。

ALPHA / DIGIT / “-” / “.” / “_” / “~”

α/数字/”-”/”.”/”_”

第3.4节也解释了因为查询往往会本身包含一个URL,最好不要百分号编码斜杠(“/”)和问号(“?”)。这也是受欢迎的iOS HTTP网络库 Alamofire 采取的方法,这给了我信心。

因此,用RFC 3986编码一个兼容性的查询,我们可以百分号编码如上所述以外的所有字符。这很简单,如果我们首先构建一组允许的字符,然后用stringByAddingPercentEncodingWithAllowedCharacters去编码剩余的。

注意:苹果已经在iOS 9中弃用了stringByAddingPercentEscapesUsingEncoding或CFURLCreateStringByAddingPercentEscapes这两个方法。

Swift

首先,swift String extension:

extension String {     func stringByAddingPercentEncodingForRFC3986() -> String? {         let unreserved = "-._~/?"         let allowed = NSMutableCharacterSet.alphanumericCharacterSet()         allowed.addCharactersInString(unreserved)         return stringByAddingPercentEncodingWithAllowedCharacters(allowed)     } }

Object-C

我们可以用Object-C的NSString的分类来做相同的事。

@implementation NSString (URLEncoding) - (nullable NSString *)stringByAddingPercentEncodingForRFC3986 {     NSString *unreserved = @"-._~/?";     NSMutableCharacterSet *allowed = [NSMutableCharacterSet alphanumericCharacterSet];     [allowed addCharactersInString:unreserved];     return [self stringByAddingPercentEncodingWithAllowedCharacters: allowed]; } @end

用例

// Swift  let query = "one&two =three"  let encoded = query.stringByAddingPercentEncodingForRFC3986()  // "one%26two%20%3Dthree"    // Objective-C  NSString *query = @"one&two =three";  NSString *encoded = [query stringByAddingPercentEncodingForRFC3986];  // "one%26two%20%3Dthree"

对x-www-form-urlencoded进行编码

推荐 W3C HTML5 对表单数据编码是相似的,但是和RFC 3986有一点不同。在第4.10.22.5节中告诉我们下列字符是不应该百分号编码:

ALPHA / DIGIT / “*” / “-” / “.” / “_”

α/数字/”-”/”.”/”_”

你应该用“+”(0x2B)代替空格(“ ”)。它和RFC 3986 的不同在 Stack Overflow answer 里有描述。波浪号(“~”)被百分号编码了,但是星号(“*”)没有。该建议很好地总结了这种情况: 这种编码的表单数据在很多方面是异常的,多年来的实践的问题和折中解决导致了互通性的一系列必要操作。但是绝不代表好的设计实践。

Swift

给String extension添加一个新的方法

public func stringByAddingPercentEncodingForFormData(plusForSpace: Bool=false) -> String? {   let unreserved = "*-._"   let allowed = NSMutableCharacterSet.alphanumericCharacterSet()   allowed.addCharactersInString(unreserved)    if plusForSpace {     allowed.addCharactersInString(" ")   }    var encoded = stringByAddingPercentEncodingWithAllowedCharacters(allowed)   if plusForSpace {     encoded = encoded?.stringByReplacingOccurrencesOfString(" ",                         withString: "+")   }   return encoded }

注意,由于很多 web服务好像不关心我用“+”或者百分号编码将空格做了可选的编码。

Object-C

Object-C的方法缺少一个可选参数

- (nullable NSString *)stringByAddingPercentEncodingForFormData:(BOOL)plusForSpace {     NSString *unreserved = @"*-._";     NSMutableCharacterSet *allowed = [NSMutableCharacterSet                                     alphanumericCharacterSet];     [allowed addCharactersInString:unreserved];     if (plusForSpace) {         [allowed addCharactersInString:@" "];     }     NSString *encoded = [self stringByAddingPercentEncodingWithAllowedCharacters:allowed];     if (plusForSpace) {         encoded = [encoded stringByReplacingOccurrencesOfString:@" " withString:@"+"];     }     return encoded; }

用例:

// Swift let query = "one two" let space = query.stringByAddingPercentEncodingForFormData() // "one%20two"  let plus = query.stringByAddingPercentEncodingForFormData(true) // "one+two"  // Objective-C NSString *query = @"one two"; NSString *encodedQuery = [query stringByAddingPercentEncodingForFormData:YES]; // "one+two"

源代码

Swift代码和一些测试用例你可以在我的Github代码实例库的 Encode 项目里找到,Object-C的分类和测试用例在 TwitterSearch 项目里。欢迎反馈和改进。

深入阅读

  • 百分号编码(维基百科)

  • RFC 3986同一资源标识符(URI):通用语法

  • W3C HTML5 见第4.10.22.6 节URL表单数据的编码

原文  http://www.cocoachina.com/ios/20160329/15814.html
正文到此结束
Loading...