一、问题引出

在iOS中,假如WKWebview跳转的链接不带参数可是带了#网页锚点,而你这边项目因为要兼容一切跳转链接,对链接进行了百分比编码,将#编码为了23%, 那么将呈现”无法显现网页“或空白网页的状况。

iOS webview跳转链接带#问题

一起满足下面3个条件会呈现这个问题:

  • 装备的广告跳转链接中带了#符号,即有网页锚点。
  • 链接中是没有参数部分的,即?param1=value1&之类的。
  • webview加载这个链接之前,对链接全体进行了百分比编码,“#”符号被编码为”23%“

在实践的场景中,产品或运维装备广告链接时,有时需求翻开网页后跳转到某个元素节点的,也就是有链接中带#这种需求的。

为了兼容他们装备带#链接这种状况,咱们iOS这边需求代码上做兼容。

二、问题根因

1. 链接中#的效果

一般用于较长网页中,跳转到网页中的某个节点。

iOS webview跳转链接带#问题

2. 对装备链接进行调试探索

拿一个链接进行举例: “/post/717682…” ,

进行百分比编码后:
  • 编码后变为:https:///post/717682356705977963923%heading-4,加载失利。
  • 删掉#锚点内容后,恳求途径https:///post/7176823567059779639,加载成功。
关于上述链接”#”不进行编码:
  • 直接能加载成功, 并且跳转到锚点‘heading-4’。
  • 假如锚点名称写错了,如‘heading-4’写成了‘heading-400’,那么也能加载成功,只不过不会跳到锚点。

那么为什么#被编码为23%之后,就不能恳求成功呢?

3. 链接中#是否被编码,服务器收到恳求时有何异同?

咱们对链接进行百分比编码后,通过Charles抓包恳求的结果:

iOS webview跳转链接带#问题
能够看到:

  • 假如#编码为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中除去通用分隔符和子分隔符后生成,也就是说是体系字符集的一个子集,关于这个问题也是行不通的!!!