我有以下Objective-C代码:
[@"http://www.google.com" stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]];
// http%3A//www.google.com
然而,在斯威夫特:
"http://www.google.com".addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)
// http://www.google.com
我可以将这种差异归结为什么?
..并且为了额外的功劳,我可以依赖此代码来编码url路径保留字符,同时传递这样的完整网址吗?
答案 0 :(得分:4)
问题实际上取决于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 /栏:
页面第一部分中的:
是百分比编码,但后续细分中的:
不是。这捕获了如何在每RFC 3986的相对路径中处理冒号的逻辑。
String
方法addingPercentEncoding(withAllowedCharacters:)
不会这样做:
let string = "foo:/bar:"
os_log("%@", string.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!)
收率:
FOO:/栏:
显然,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
之间qux
:
值得注意的是,NSURLComponents
有一个小但却相当基本的缺陷:具体来说,如果您有查询值NSURLQueryItem
,可能有+
个字符,大多数网络服务需要转义百分比,但NSURLComponents
不会。如果您的网址包含查询组件,并且这些查询值可能包含+
个字符,我建议不要使用NSURLComponents
,而是建议您自己对网址的各个组件进行百分比编码。