前言
在使用WKWebview的时候,通常都离不开URL,一般的符合网络标准的URL没有什么问题,但是在公司开发的时候遇到了一些特殊URL的时候就踩到了URL编码的坑。
在1994年订制的RFC1738文档中,对字符串中的除了- _ .
之外的所有非字母数字字符都替换成百分号(%)后跟两位十六进制数,十六进制数中字母必须为大写。
在2005年定义的RFC3986中,将针对- _.~
四个字符之外的所有非字母数字字符进行百分号编码。当然 根据URL的类型不同,有也一部分预留字符不需要进行编码,例如查询的URL
中可以包含? /
字符,不需要转义。更详细文档的可以查看RFC 3986。
Swift URL Encode
addingPercentEncoding(withAllowedCharacters:
是iOS7之后出现的新API用于url encode
。
官方对该方法的解释:
// Returns a new string made from the receiver by replacing all characters not in the allowedCharacters set with percent encoded characters. UTF-8 encoding is used to determine the correct percent encoded characters. Entire URL strings cannot be percent-encoded. This method is intended to percent-encode an URL component or subcomponent string, NOT the entire URL string. Any characters in allowedCharacters outside of the 7-bit ASCII range are ignored.
最后一句Any characters in allowedCharacters outside of the 7-bit ASCII range are ignored.,意思就是说,任何非7-bit ASCII字符搁到allowedCharacters里面也将被忽略,也就是allowedCharacters
里面的字符跟7-bit ASCII字符不会被编码。
换句话说,上面方法在处理的时候会编码url的中的非7-bit ASCII字符,如这些【`#%^{}”[]|\<>】,如果需要忽略之,需要通过.allowedCharacters这个参数指定。
然而并不是,我们来看一下案例:
1 | let url1 = "http://github.com#aaa?name=中文字符&key=!*'();:@&=+$,/?%#[]" |
输出结果:
1 | %68%74%74%70%3A%2F%2F%67%69%74%68%75%62%2E%63%6F%6D#%61%61%61%3F%6E%61%6D%65%3D%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6%26%6B%65%79%3D%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%#[] |
可以看到只有后面的%#[]没有被编码,其余都被编码了,不是我们之前所理解的“allowedCharacters里面的字符跟7-bit ASCII字符不会被编码”,而是只有allowedCharacters里的字符才不会被编码
正确的方法:使用inverted
1 | let url1 = "http://github.com#aaa?name=中文字符&key=!*'();:@&=+$,/?%#[]" |
输出结果:
1 | let url1 = "http://github.com#aaa?name=中文字符&key=!*'();:@&=+$,/?%#[]" |
可以看到,通过集合反转之后得到的结果才是我们想要的,但是此处的意思是反的,就是对集合进行inverted,表示集合内的字符和非7-bit ASCII字符是需要转码的,所以我们以后使用这个方法进行转码的时候要从反面进行转码,把想要进行转码的特殊字符写在集合里就好了,注意这里说的是想要转码的特殊字符(!*‘();:@&=+$,/?%#[]),中文会被认为是非7-bit ASCII字符会自动转码的。
CharacterSet
CharacterSet
是一个结构体,CharacterSet.urlHostAllowed
等预制类型包含了所有不需要被转码的字符,反过来说就是指明了需要被转码的字符。CharacterSet
类中提供了一些常用的URL转码的类型:
1 | CharacterSet.urlHostAllowed: 被转义的字符有 "#%/<>?@\^`{|} |
使用
编码
1 | let url1 = "http://github.com?name=中文字符&key=!*'();:@&=+$,/?%#[]" |
输出结果:
1 | http://github.com?name=%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6&key=!*'();:@&=+$,/?%25%23%5B%5D |
解码
1 | var encodeUrl1 = "http://github.com%23aaa?name=%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6&key=!*'();:@&=+$,/?%25%23%5B%5D" |
输出结果:
1 | http://github.com#aaa?name=中文字符&key=!*'();:@&=+$,/?%#[] |
URL带有#符号的问题
#是url中的一个重要组成部分,是跟在url参数之后的的最后一部分,作为一个url的锚点,用于浏览器的定位。
但是在项目中使用的时候发现#号被转义掉了,前端那边就没有办法正常显示。所以需要其他特殊字符正常转义,除去#号。
方法一
沿用预制类型,采用insert方法
1 | var urlStr = "http://test.com/中文/main.html#/help" |
这里采用了预制的,insert会反过来被删除掉
至于insert的原因,可以查看一下以下代码
1
2
3
4
5
6
7
8
9
10
11
12
13 > let url1 = "http://github.com#aaa?name=中文字符&key=!*'();:@&=+$,/?%#[]"
> print("-----------------未反转情况-------------------------")
> var s1 = CharacterSet(charactersIn: #"[]"#)
> print(url1.addingPercentEncoding(withAllowedCharacters: s1.inverted)!)
> s1.insert(charactersIn: "[")
> print(url1.addingPercentEncoding(withAllowedCharacters: s1.inverted)!)
>
> print("-----------------反转情况-------------------------")
> var s2 = CharacterSet(charactersIn: #"[]"#).inverted
> print(url1.addingPercentEncoding(withAllowedCharacters: s2)!)
> s2.insert(charactersIn: "[")
> print(url1.addingPercentEncoding(withAllowedCharacters: s2)!)
>
输出结果,留意最后的[]符号是否被转义
1
2
3
4
5
6
7
8 > -----------------未反转情况-------------------------
> http://github.com#aaa?name=%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6&key=!*'();:@&=+$,/?%#%5B%5D
> http://github.com#aaa?name=%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6&key=!*'();:@&=+$,/?%#%5B%5D
> -----------------反转情况-------------------------
> http://github.com#aaa?name=%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6&key=!*'();:@&=+$,/?%#%5B%5D
> http://github.com#aaa?name=%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6&key=!*'();:@&=+$,/?%#[%5D
> Program ended with exit code: 0
>
还未找到解释理由
方法二
自定义
1 | var urlStr = "http://test.com/中文/main.html#/help" |
Json的URL编码
我公司有的链接需要拼接上一大段的json,里面json还有复杂的&等符号,预制的肯定不够用,只能采用自定义方法:
所以封装了方法:
1 | extension String { |
直接使用即可
1 | var urlStr = "http://test.com/中文/main.html#/help" |
题外话
Swift 5 字符串转义字符处理
增加了 # 符号,使得写字符串更加简单。
在字符串中包含 “ 时不必再加 \
1 | //before |
包含 \ 反斜杠也不需要再加转义符
1 | //before |
由于反斜杠作为字符串中的字符,所以在插入值的时候需要在后面再加个 #
1 | //before |
当字符串包含 # 时, 前后应用 ## 包裹字符串
1 | let str = ##"this is "a"#good ideal"## |
用 #””” 开头 “””#结尾 来表示多行字符串
1 | let multiline = #""" |
由于不用反斜杠转义 使得正则表达式更加简洁明了
1 | //before |
Objective-C url encode
API调用都是一样的,不过网上流传的比较多的是用的C API
1 | NSString *ciphertext = @"saf#*&"; |
C API
1 | NSString *ciphertext = @"saf#*&"; |
参考资料
https://www.jianshu.com/p/c135127a3df2
https://www.jianshu.com/p/74f7c5bbca50