注册
iOS

iOS webview跳转链接带#问题

一、问题引出


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


bb21f2360e5ba913a2e117584e9047c6.png

同时满足下面3个条件会出现这个问题:

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

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


为了兼容他们配置带#链接这种情况,我们iOS这边需要代码上做兼容。


二、问题根因


1. 链接中#的作用


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

6907f5e2f76c0272aa8619050a5d2601.png


2. 对配置链接进行调试探索


拿一个链接进行举例:
"juejin.cn/post/717682…" ,


进行百分比编码后:

对于上述链接"#"不进行编码:
  • 直接能加载成功, 并且跳转到锚点‘heading-4’。
  • 如果锚点名称写错了,如‘heading-4’写成了‘heading-400’,那么也能加载成功,只不过不会跳到锚点。

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


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


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

62773ca501500b040151dbd16f208c9d.png

可以看到:

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


作者:sweet丶
链接:https://juejin.cn/post/7256014547067682853
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册