Objective-C 和 Swift 之间的 URL 编码方法不一致

Inconsistencies in URL encoding methods across Objective-C and Swift

我有以下 Objective-C 代码:

[@"http://www.google.com" stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]];
// http%3A//www.google.com

然而,在 Swift 中:

"http://www.google.com".addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)
// http://www.google.com

我可以将这种差异归因于什么?

..为了获得额外的奖励,我可以依靠此代码对 url 路径保留字符进行编码,同时像这样传递完整的 url 吗?

问题实际上在于NSString方法stringByAddingPercentEncodingWithAllowedCharactersString方法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/bar:

页面第一段中的 : 是百分比编码,但后续段中的 : 不是。这捕获了如何根据 RFC 3986.

处理相对路径中的冒号的逻辑

String 方法 addingPercentEncoding(withAllowedCharacters:) 但是,不会这样做:

let string = "foo:/bar:"
os_log("%@", string.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!)

产量:

foo:/bar:

显然,String 方法不会尝试那种位置敏感的逻辑。这个实现更符合方法的名称(它只考虑哪些字符是 "allowed",没有试图猜测的特殊逻辑,基于允许的字符出现的位置,它是否真的被允许。)


我了解到您对问题中提供的代码感到厌烦,但我们应该注意,这种在相对路径中转义冒号的行为虽然对解释您的经历很有趣,但与您眼前的问题并不真正相关.您提供的代码根本不正确。它试图对 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 值内正确地转义百分比,但它正确地将 & 留在 [=28= 之间] 和 qux:

http://httpbin.org/post?foo=bar%20%26%20baz&qux=42

但值得注意的是,NSURLComponents 有一个小但相当基本的缺陷:具体来说,如果您有查询值,NSURLQueryItem,它可能有 + 个字符,大多数 Web 服务需要转义百分比,但 NSURLComponents 不需要。如果您的 URL 有查询组件,并且如果这些查询值可能包含 + 个字符,我建议您不要使用 NSURLComponents,而是建议对 URL 的各个组件进行百分比编码你自己。