let path1 = "[path:test]"
var set = CharacterSet()
set.formUnion(.urlPathAllowed)
let encodedPath1 = path1.addingPercentEncoding(withAllowedCharacters: set)
let encodedPath2 = path1.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)
print("Encoded Path 1: \(String(describing: encodedPath1))")
print("Encoded Path 2: \(String(describing: encodedPath2))")
打印是(:在第一个中没有编码。)
Encoded Path 1: Optional("%5Bpath:test%5D")
Encoded Path 2: Optional("%5Bpath%3Atest%5D")
根据 RFC 3986,只有第二种编码有效。相对路径(即您的字符串)中允许使用冒号,第一个路径组件除外(即第一个斜杠之前)。
addingPercentEncoding
处理内置 URL 相关字符类的方式与您在代码中创建的随机字符类不同。您在代码中创建的字符类无法编码复杂的逻辑,例如“允许使用冒号,除非它位于第一个斜杠之前”,即使它包含与内置 URL 字符类相同的字符。
您可以通过查看 swift-corelibs-foundation 存储库中的
_CFStringCreateByAddingPercentEncodingWithAllowedCharacters
函数来了解其工作原理。这并不完全是在 Apple 平台上运行的,但它是一个很好的参考。
GetURLPredefinedCharacterSet
来查看传入的字符集是否是内置字符集之一。至关重要的是,GetURLPredefinedCharacterSet
与内置字符集进行指针比较:
for ( result = 0; result < kURLAllowedCharacterSetIllegal; ++result ) {
// yes, I really want a pointer comparison because some of the sURLAllowedCharacterSets have the same bitmaps
if ( characterSet == sURLAllowedCharacterSets[result] ) {
break;
}
}
它不会在
set
数组中找到您的 sURLAllowedCharacterSets
,因此它将返回 kURLAllowedCharacterSetIllegal
,并且 _CFStringCreateByAddingPercentEncodingWithAllowedCharacters
会将您的集合视为没有特殊含义的常规字符集,并且不会转义冒号,因为冒号字符在集合中。
如果您使用内置的
.urlPathAllowed
,_CFStringCreateByAddingPercentEncodingWithAllowedCharacters
将沿着不同的路径走下去,并到达这里,在那里完成特殊处理。
我们可以看到,在
else
的 if (pastSlash)
分支中,它明确禁止使用冒号:
Boolean allowed = (ch <= 127) && (ch != ';') && (ch != ':') && ((sURLAllowedCharacters[ch] & allowedMask) != 0);
而在
if
分支中,没有 && (ch != ';')
。