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