WKURLSchemeHandler 的能与不能

1. 背景

在作者将 App 里的 UIWebView 切换到 WKWebView 之后,有些功能得到了加强,包括烦人的 Cookie 也很好的解决了,唯独离线包和 webView 里的资源请求拦截一直是个心病,没有很优雅的解决方案。

坊间流传的自定义拦截 https、http 的方案,可以实现一部分功能。但是有很大的缺点,

  1. 调用私有的 API,审核和以后版本升级有隐患
  2. XHR 请求丢失 body 的问题,所以要封装一层 myXHR 代替 XHR
  3. 拦截 form 请求里的 body 参数。

而缓存和资源拦截作为 webView 框架应该提供的基本功能,AppHost【注1】也尝试用一个更优雅的方式来实现,所以尝试用 iOS11+ 上提供的新接口。

2. WKURLSchemeHandler 的 checklist

WKURLSchemeHandler 被引入是在WWDC 2017 Customized Loading in WKWebView ,当时演示的时候是以加载自定义图片为例的。

请记住这句话“加载自定义图片为例”。

结合我们的需求,我们需要加载本地资源拦截请求发出 ajax 请求,同时这些请求都包含正确的** Cookie **,我们需要解决以下几个子问题;

- 自定义 scheme ,可以定义哪些 scheme?

根据自己测试和阅读源码,下面类型的都属于内置协议不可用

static const StringVectorFunction functions[] {
            builtinSecureSchemes,
            builtinSchemesWithUniqueOrigins,
            builtinEmptyDocumentSchemes,
            builtinCanDisplayOnlyIfCanRequestSchemes,
            builtinCORSEnabledSchemes,
        };

结论:已知的约定俗称的都不能定义,包括,https、http、about,当然也包括,data、blob、ftp 等,如果你这样做了,会收到一个错误''https' is a URL scheme that WKWebView handles natively'

- 自定义 scheme ,哪些 HTML 里元素会触发自定义请求?

根据作者在常见的 html 元素里搜集的会触发资源下载或者 navigation 逻辑的标签,整理了下面的表格。

标签 domain 是 http 情况 domain 是 https 情况 是否触发 decidePolicyForNavigation
(此时意味着 webkit 有 bug)
img 的 src --
script 的 src --
link 的 href --
iframe 的 src --
css 的 background-image 语法 --
css 的 cursor 语法 --
object 标签的 data 属性 --
audio 标签的 src 属性 --
video 标签的 source 属性 --
a 标签的 href 属性 😈
xhr 的 url
form post 😈
form get 😈

总结一下;按照 MDN 里混合内容的说法,混合被动/显示内容在任何情况下都会触发,混合活动内容 在 https 下不会触发。

在 https 下的 xhr 的请求和混合活动内容,都不能触发的原因其实是两个机制;

  1. 混合活动内容是受限 CSP,目前基本无法绕过。
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; script-src 'self' apphost: ;">

上面的写法并没有生效,依然阻止 apphost://a.js 的文件加载。(可能是我姿势不对)

  1. xhr 不能发出去,原因是 same-origin 策略导致的。

Definition of an originSection
Two URLs have the same origin if the protocol, port (if specified), and host are the same for both.

- 自定义 scheme ,可以带 Cookie 吗?

作者通过网上搜索、阅读了 RFC6265 里对 Cookie 的规范、实践,得出了以下结论。

  1. 自定义 scheme 是可以带 Cookie,而且和相同 domain 的其他协议共享 Cookie。这就厉害了,然后实际上不完全是——初步测试,http,https,ftp等 scheme 其实是完全没问题,包括设置 session 级别的 Cookie;在此之外,设置自定义 scheme 的 session Cookie 会 fail,持久化 Cookie 是可行的。 没想到吧

- 自定义 scheme ,可以发 post 请求吗?

是的。在 webkit 实现有 bug 的情况下,可以用 form 发送 post 请求。不仅如此,使用 form post,可以成功将 body 发送到 native,这个就厉害了。

- 自定义 scheme ,可以拦截 xhr 请求?是否丢失 POST 请求的 body?

事实上是可以拦截的。在上面表格里虽然 http、https 下都是 ❌,但是那是因为 domain 是标准协议。如果是

[self.webView loadHTMLString:html baseURL:[NSURL URLWithString:@"apphost://test.com/index.2.html"]];

那么 xhr 依然也可以发送出去,但是不幸的时,xhr 里 body 还是丢了。

3 实践

有了上述的前提。我们有一个简单的离线包渲染的方案。

  1. 把加载页面的 domain 改为 自定义协议如 apphost;
[self.webView loadHTMLString:html baseURL:[NSURL URLWithString:@"apphost://test.com/index.2.html"]];
  1. 加载的 HTML 文件应用的资源全部用://user/a.png这种相对的写法。这样 http 和 自定义协议都可以用。
  2. HTML 里面 ajax 请求使用绝对路径,修改后端的 Response 头,使用 Cross-Origin Resource Sharing (CORS) 技术,配合对 ajax 请求追加几个参数,也可以实现带上 Cookie 的请求

上面的做法,可以拦截基本上传统 UIWebview 时代可拦截的所有类型。但是缺点也很明显。

  • iOS 11+ 以上才能用
  • https 下支持有限
  • 对 xhr js 代码小幅度的改动
  • 最最严重的是,不支持 session 级别的 Cookie 设置。

在 AppHost 框架里,作者没有采用这种方式,而是采用了读 html,解析静态资源的方式来实现加载离线包的功能,同时不影响 session 级别的 Cookie 设置。

总的来说 WKURLSchemeHandler 的使用场景还是比较有限,不能拦截 http 请求,有些结合 NSURLSession,先下载 html 的方式,然后再代理的方式,对 webview 的加载速度反而是拖累,还需要想想其它办法。

结论:WKURLSchemeHandler 最好的场景还是用在加载本地图片上,其它方面不稳定,慎用。

4. 注

  1. AppHost.framework 是作者在网易有钱、严选工作中,抽离出来 JSBridge 的库,实现 native 和 h5 之间的通讯,内置诸多常用的功能,在业务简单的情况下可开箱即用,复杂情况允许灵活定制。预计在4月底开源

5.参考

  1. network-scheme 的定义
  2. RFC6265里对 Cookie domain 的说明
  3. Preflighted requests
  4. 关于 ajax 跨域携带 Cookie
  5. 测试 demo 源码
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容