NSString
问题实际上在于methodstringByAddingPercentEncodingWithAllowedCharacters
和String
method之间的区别addingPercentEncoding(withAllowedCharacters:)
。而且这种行为一直在随着版本的变化而变化。(看起来 iOS 11 的最新测试版现在恢复了我们过去看到的这种行为。)
我相信问题的根源在于路径百分比编码的细节。RFC 3986的第 3.3 节说,除了相对路径的第一段之外,路径中允许使用冒号。
该NSString
方法捕获了这个概念,例如想象一个路径,其第一个目录是foo:
(带有冒号)和子目录bar:
(也带有冒号):
NSString *string = @"foo:/bar:";
NSCharacterSet *cs = [NSCharacterSet URLPathAllowedCharacterSet];
NSLog(@"%@", [string stringByAddingPercentEncodingWithAllowedCharacters:cs]);
这导致:
foo%3A/条:
页面的:
第一段中的 是百分比编码的,但:
后续段中的不是。这捕获了如何根据RFC 3986处理相对路径中的冒号的逻辑。
但是,该String
方法addingPercentEncoding(withAllowedCharacters:)
不这样做:
let string = "foo:/bar:"
os_log("%@", string.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!)
产量:
富:/酒吧:
显然,该String
方法没有尝试那种位置敏感的逻辑。此实现更符合方法的名称(它仅考虑“允许”哪些字符,没有尝试猜测的特殊逻辑,基于允许字符出现的位置,是否真正允许。)
我推测您对问题中提供的代码感到很困扰,但我们应该注意,这种在相对路径中转义冒号的百分比行为虽然对解释您的经历很有趣,但与您的直接问题并不真正相关。您提供的代码完全不正确。它试图对 URL 进行百分比编码,就好像它只是一个路径一样。但是,这不是一条路径。它是一个 URL,它有自己的规则是另一回事。
对百分比编码 URL 的更深入了解是承认 URL 的不同组件允许不同的字符集,即它们需要不同的百分比编码。这就是为什么NSCharacterSet
有这么多不同的与 URL 相关的字符集。
您确实应该对各个组件进行百分比编码,并使用该类型组件允许的字符集对每个组件进行百分比编码。只有当单个组件被百分比编码时,它们才应该被连接在一起以形成整个 URL。
或者,NSURLComponents
正是为此目的而设计的,让您摆脱自己对各个组件进行百分比编码的麻烦。例如:
var components = URLComponents(string: "http://httpbin.org/post")!
let foo = URLQueryItem(name: "foo", value: "bar & baz")
let qux = URLQueryItem(name: "qux", value: "42")
components.queryItems = [foo, qux]
let url = components.url!
这产生了以下内容,其中&
和 两个空格在值内正确地转义了foo
百分比,但它正确地留下了&
中间的foo
and qux
:
http://httpbin.org/post?foo=bar%20%26%20baz&qux=42
不过值得注意的是,它NSURLComponents
有一个很小但相当基本的缺陷:具体来说,如果您有查询值 ,NSURLQueryItem
可能包含+
字符,大多数 Web 服务需要转义该百分比,但NSURLComponents
不会。如果您的 URL 具有查询组件,并且如果这些查询值可能包含+
字符,我会建议您不要NSURLComponents
,而是建议您自己对 URL 的各个组件进行百分比编码。