iOS 17 NSURL 百分比获得双重编码

iOS 17 NSURL percentage gets double encoded

提问人:Chen Xiaofeng 提问时间:10/31/2023 最后编辑:Chen Xiaofeng 更新时间:10/31/2023 访问量:168

问:

在 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 似乎没有要求对百分比进行编码

IOS目标 -C NSURL iOS17

评论

1赞 HangarRash 10/31/2023
use 来构建 URL,而不是 using。NSURLComponentsstringByAddingPercentEncodingWithAllowedCharacters
0赞 Codo 10/31/2023
请出示您的代码,以便我们了解您自己的编码代码如何与调用方法相结合。从目前的描述中看不出这一点。NSURL
0赞 Chen Xiaofeng 10/31/2023
@Codo,我只是在我的例子上添加更多谢谢
0赞 Larme 10/31/2023
你应该使用 (NS)URLComponents,以及 NSURLQueryItem 等,或者如果你想对自己进行编码,你应该一块一块地进行:主机(通常不需要),每个参数的参数(通常是参数的值),然后追加。
0赞 Cy-4AH 11/1/2023
我认为只是变得更聪明:需要编码 - 是的,然后传递的字符串没有被编码,整个路径被编码。请通过编码和URLWithString[[]

答:

3赞 Codo 10/31/2023 #1

对 URL 编码的最大误解是它对 URL 进行编码。事实并非如此。它仅适用于路径组件和查询参数。

无法对整个 URL 进行编码。如果它已经是一个 URL,则不需要编码。如果它不是 URL,那么应该如何处理它?哪些部分应被视为主机名、路径和查询参数?

这就是为什么 的行为是有问题的。根据文档:NSURL URLWithString:

NSURL 会自动对无效字符进行百分比和 IDNA 编码,以帮助创建有效的 URL。

因此,它会尝试修复无效的 URL。但是由于 URL 无效,这不能可靠地工作。

对于您的情况,它不起作用。你给它这个无效的URL:

http://google.com/ad/1?asseturl=[ASSETURI]&partner=abc%2Fdev

查询参数的值无效(方括号需要编码),而值可能有效或无效。这基本上是猜测是否需要对 URL 进行编码的值。这是苹果的实现已经改变。asseturlpartnerpartner

这是触发双重编码的无效参数。如果没有无效的参数,则不会再次对 的值进行编码。asseturlpartner

在任何编程语言和任何框架或库中,正确的方法是分别对每个路径组件和每个查询值进行编码。

使用此方法,首先会构建一个有效的 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