iOS webview跳转链接带#问题
一、问题引出
在iOS中,如果WKWebview
跳转的链接不带参数但是带了#
网页锚点,而你这边项目因为要兼容所有跳转链接,对链接进行了百分比编码,将#
编码为了23%
, 那么将出现”无法显示网页“或空白网页的情况。
同时满足下面3个条件会出现这个问题:
- 配置的广告跳转链接中带了
#
符号,即有网页锚点。 - 链接中是没有参数部分的,即
?param1=value1&
之类的。 webview
加载这个链接之前,对链接整体进行了百分比编码,“#”
符号被编码为”23%“
。
在实际的场景中,产品或运维配置广告链接时,有时需要打开网页后跳转到某个元素节点的,也就是有链接中带#
这种需求的。
为了兼容他们配置带#
链接这种情况,我们iOS这边需要代码上做兼容。
二、问题根因
1. 链接中#
的作用
一般用于较长网页中,跳转到网页中的某个节点。
2. 对配置链接进行调试探索
拿一个链接进行举例:
"juejin.cn/post/717682…" ,
进行百分比编码后:
- 编码后变为:
https://juejin.cn/post/717682356705977963923%heading-4
,加载失败。 - 删掉
#
锚点内容后,请求路径https://juejin.cn/post/7176823567059779639
,加载成功。
对于上述链接"#"不进行编码:
- 直接能加载成功, 并且跳转到锚点‘heading-4’。
- 如果锚点名称写错了,如‘heading-4’写成了‘heading-400’,那么也能加载成功,只不过不会跳到锚点。
那么为什么#被编码为23%之后,就不能请求成功呢?
3. 链接中#是否被编码,服务器收到请求时有何异同?
我们对链接进行百分比编码后,通过Charles
抓包请求的结果:
可以看到:
- 如果
#
编码为23%
,则服务器收到的请求路径也是带23%
. - 如果是未编码
#
,则服务器收到的请求路径是不带#
后面的内容的。
这也就是说,对于iOS端来说,客户端发送请求时未发送#及后面的内容,但是会发送23%及后面的内容。 具体的响应是服务器决定的。
其中#
编码为23%
的两种情况:
23%
后面还有/
, 比如https:www.xxx.com/path1/path23%/
23%
后面没有/
,比如https:www.xxx.com/path1/path23%
或https:www.xxx.com/path1/path23%section1
第一种情况下,有的网页能加载出来,有的网页会找不到网页,能否加载成功是根据服务器能否找到网页来定;第二种加载会失败,原因是23%
也被服务器拿去查找资源路径。
我相信到这里,应该已经解释清楚了问题发生的原因。
三、兼容链接#的解决方案
我们客户端APP上显示的营销广告链接都是来源于后台配置的,有时配置的链接是有需要跳到锚点的需求的,那么我们该怎么兼容呢?
- 需要对链接进行百分比编码.
- 百分比编码时需要屏蔽掉
#
.
解决方案
let url = "https://juejin.cn/post/7176823567059779639#heading-4"
var notEncodeSet = CharacterSet.urlQueryAllowed
// 关键代码:
// 在对链接进行百分比编码时,不编码字符集中追加#
notEncodeSet.insert(charactersIn: "#")
if let urlPath = url.addingPercentEncoding(withAllowedCharacters: notEncodeSet) {
// 一般会有对path追加自定义公参或者设置自定义请求头之类的事情...
let URL = URL(string: urlPath)!
let request = MutableURLRequest(url: URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10)
// 具体的加载
webview.load(request as URLRequest)
}
使用Alamofire的字符编码不能解决问题
在找到上述原因后,我们可能会考虑使用Alamofire
的字符集CharacterSet.afURLQueryAllowed
使用来代替系统的CharacterSet.urlQueryAllowed
去编码,但这样有用吗?
首先来看下CharacterSet.afURLQueryAllowed
是怎么生成的:
public static let afURLQueryAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
let encodableDelimiters = CharacterSet(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return CharacterSet.urlQueryAllowed.subtracting(encodableDelimiters)
}()
可以看到是由CharacterSet.afURLQueryAllowed
中除去通用分隔符和子分隔符后生成,也就是说是系统字符集的一个子集,对于这个问题也是行不通的!!!
链接:https://juejin.cn/post/7256014547067682853
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。