一、问题引出
在iOS中,假如WKWebview
跳转的链接不带参数可是带了#
网页锚点,而你这边项目因为要兼容一切跳转链接,对链接进行了百分比编码,将#
编码为了23%
, 那么将呈现”无法显现网页“或空白网页的状况。
一起满足下面3个条件会呈现这个问题:
- 装备的广告跳转链接中带了
#
符号,即有网页锚点。 - 链接中是没有参数部分的,即
?param1=value1&
之类的。 -
webview
加载这个链接之前,对链接全体进行了百分比编码,“#”
符号被编码为”23%“
。
在实践的场景中,产品或运维装备广告链接时,有时需求翻开网页后跳转到某个元素节点的,也就是有链接中带#
这种需求的。
为了兼容他们装备带#
链接这种状况,咱们iOS这边需求代码上做兼容。
二、问题根因
1. 链接中#
的效果
一般用于较长网页中,跳转到网页中的某个节点。
2. 对装备链接进行调试探索
拿一个链接进行举例: “/post/717682…” ,
进行百分比编码后:
- 编码后变为:
https:///post/717682356705977963923%heading-4
,加载失利。 - 删掉
#
锚点内容后,恳求途径https:///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:///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
中除去通用分隔符和子分隔符后生成,也就是说是体系字符集的一个子集,关于这个问题也是行不通的!!!