提问人:Chen Xiaofeng 提问时间:10/31/2023 最后编辑:Chen Xiaofeng 更新时间:10/31/2023 访问量:168
iOS 17 NSURL 百分比获得双重编码
iOS 17 NSURL percentage gets double encoded
问:
在 iOS 17 中,NSURL 解析更改为使用 RFC 1738/1808 中的 RFC 3986
(https://developer.apple.com/documentation/foundation/nsurl/1572047-urlwithstring)。根据 RFC 3986 指南,保留字符包含 gen-delims = 和 sub-delims =":" / "/" / "?" / "#" / "[" / "]" / "@"
"!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
其中gen-delims需要编码,sub-delims根据应用是可选的。 iOS 17 似乎只对 gen-delims 进行编码,但我们的应用程序也要求在我们的宏扩展逻辑中对 sub-delims 进行编码。 在 iOS 17 之前,这不是问题,因为我们依赖自己的宏值编码逻辑
[str stringByAddingPercentEncodingWithAllowedCharacters:[[NSCharacterSet characterSetWithCharactersInString:@" !*'\"();:@&=+$,/?%#[]^|{}\\`"] invertedSet]];
但不知何故,现在所有百分比都进行了双重编码,这引起了问题。
例如,下面的 url 我们想将宏 [PARTNER] 扩展到 abc/dev
http://google.com/ad/1?asseturl=[ASSETURI]&partner=[PARTNER]
//Macro expansion only for PARTNER because don't have value for ASSETURI
http://google.com/ad/1?asseturl=[ASSETURI]&partner=abc/dev
//encode the Macro value with above method
http://google.com/ad/1?asseturl=[ASSETURI]&partner=abc%2Fdev
//String to NSURL [NSURL URLWithString:url];
http://google.com/ad/1?asseturl=%5BASSETURI%5D&partner=abc%25%2Fdev
您可以看到 url 中还有一个额外的 %25,因为百分比在 IOS 17 中进行了双重编码,它在 iOS 17 之前不存在。
那么为什么 NSURL 会对百分比进行编码(尽管 NSURL 似乎只在存在无效字符时才这样做)?这是一个错误,因为 RFC 3986 似乎没有要求对百分比进行编码
答:
对 URL 编码的最大误解是它对 URL 进行编码。事实并非如此。它仅适用于路径组件和查询参数。
无法对整个 URL 进行编码。如果它已经是一个 URL,则不需要编码。如果它不是 URL,那么应该如何处理它?哪些部分应被视为主机名、路径和查询参数?
这就是为什么 的行为是有问题的。根据文档:NSURL URLWithString:
NSURL 会自动对无效字符进行百分比和 IDNA 编码,以帮助创建有效的 URL。
因此,它会尝试修复无效的 URL。但是由于 URL 无效,这不能可靠地工作。
对于您的情况,它不起作用。你给它这个无效的URL:
http://google.com/ad/1?asseturl=[ASSETURI]&partner=abc%2Fdev
查询参数的值无效(方括号需要编码),而值可能有效或无效。这基本上是猜测是否需要对 URL 进行编码的值。这是苹果的实现已经改变。asseturl
partner
partner
这是触发双重编码的无效参数。如果没有无效的参数,则不会再次对 的值进行编码。asseturl
partner
在任何编程语言和任何框架或库中,正确的方法是分别对每个路径组件和每个查询值进行编码。
使用此方法,首先会构建一个有效的 URL,然后传递给 .而且它不会被双重编码。NSURL URLWithString:
NSString* partner = [@"abc/dev" stringByAddingPercentEncodingWithAllowedCharacters:[[NSCharacterSet characterSetWithCharactersInString:@" !*'\"();:@&=+$,/?%#[]^|{}\\`"] invertedSet]];
NSString* asseturi = [@"[ASSETURI]" stringByAddingPercentEncodingWithAllowedCharacters:[[NSCharacterSet characterSetWithCharactersInString:@" !*'\"();:@&=+$,/?%#[]^|{}\\`"] invertedSet]];
NSURL* url = [NSURL URLWithString: [NSString stringWithFormat:@"http://google.com/ad/1?asseturl=%@&partner=%@", asseturi, partner]];
结果是:
http://google.com/ad/1?asseturl=%5BASSETURI%5D&partner=abc%2Fdev
NSURLComponents
通常是使用查询参数构造 URL 的最佳方法。他们负责编码。但是,由于您有对 sub-delims 类进行编码的额外要求,因此它们可能不是最合适的。NSURLQueryItem
评论
NSURLComponents
stringByAddingPercentEncodingWithAllowedCharacters
NSURL
URLWithString
[
[
]