WKURLSchemeHandler 的能與不能

1. 背景

在作者將 App 里的 UIWebView 切換到 WKWebView 之后琢唾,有些功能得到了加強(qiáng)闹击,包括煩人的 Cookie 也很好的解決了,唯獨(dú)離線包和 webView 里的資源請求攔截一直是個(gè)心病,沒有很優(yōu)雅的解決方案国葬。

坊間流傳的自定義攔截 https、http 的方案,可以實(shí)現(xiàn)一部分功能汇四。但是有很大的缺點(diǎn)接奈,

  1. 調(diào)用私有的 API,審核和以后版本升級有隱患
  2. XHR 請求丟失 body 的問題通孽,所以要封裝一層 myXHR 代替 XHR
  3. 攔截 form 請求里的 body 參數(shù)序宦。

而緩存和資源攔截作為 webView 框架應(yīng)該提供的基本功能,AppHost【注1】也嘗試用一個(gè)更優(yōu)雅的方式來實(shí)現(xiàn)背苦,所以嘗試用 iOS11+ 上提供的新接口互捌。

2. WKURLSchemeHandler 的 checklist

WKURLSchemeHandler 被引入是在WWDC 2017 Customized Loading in WKWebView ,當(dāng)時(shí)演示的時(shí)候是以加載自定義圖片為例的行剂。

請記住這句話“加載自定義圖片為例”秕噪。

結(jié)合我們的需求,我們需要加載本地資源硼讽、攔截請求巢价、發(fā)出 ajax 請求,同時(shí)這些請求都包含正確的** Cookie **固阁,我們需要解決以下幾個(gè)子問題壤躲;

- 自定義 scheme ,可以定義哪些 scheme备燃?

根據(jù)自己測試和閱讀源碼碉克,下面類型的都屬于內(nèi)置協(xié)議不可用

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

結(jié)論:已知的約定俗稱的都不能定義,包括并齐,https漏麦、http、about况褪,當(dāng)然也包括撕贞,data、blob测垛、ftp 等捏膨,如果你這樣做了,會收到一個(gè)錯(cuò)誤''https' is a URL scheme that WKWebView handles natively'

- 自定義 scheme 食侮,哪些 HTML 里元素會觸發(fā)自定義請求号涯?

根據(jù)作者在常見的 html 元素里搜集的會觸發(fā)資源下載或者 navigation 邏輯的標(biāo)簽,整理了下面的表格锯七。

標(biāo)簽 domain 是 http 情況 domain 是 https 情況 是否觸發(fā) decidePolicyForNavigation
(此時(shí)意味著 webkit 有 bug)
img 的 src ? ? --
script 的 src ? ? --
link 的 href ? ? --
iframe 的 src ? ? --
css 的 background-image 語法 ? ? --
css 的 cursor 語法 ? ? --
object 標(biāo)簽的 data 屬性 ? ? --
audio 標(biāo)簽的 src 屬性 ? ? --
video 標(biāo)簽的 source 屬性 ? ? --
a 標(biāo)簽的 href 屬性 ? ? ??
xhr 的 url ? ?
form post ? ? ??
form get ? ? ??

總結(jié)一下链快;按照 MDN 里混合內(nèi)容的說法,混合被動(dòng)/顯示內(nèi)容在任何情況下都會觸發(fā)眉尸,混合活動(dòng)內(nèi)容 在 https 下不會觸發(fā)域蜗。

在 https 下的 xhr 的請求和混合活動(dòng)內(nèi)容巨双,都不能觸發(fā)的原因其實(shí)是兩個(gè)機(jī)制;

  1. 混合活動(dòng)內(nèi)容是受限 CSP地消,目前基本無法繞過炉峰。
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; script-src 'self' apphost: ;">

上面的寫法并沒有生效,依然阻止 apphost://a.js 的文件加載脉执。(可能是我姿勢不對)

  1. xhr 不能發(fā)出去,原因是 same-origin 策略導(dǎo)致的戒劫。

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 嗎?

作者通過網(wǎng)上搜索迅细、閱讀了 RFC6265 里對 Cookie 的規(guī)范巫橄、實(shí)踐,得出了以下結(jié)論茵典。

  1. 自定義 scheme 是可以帶 Cookie湘换,而且和相同 domain 的其他協(xié)議共享 Cookie。這就厲害了统阿,然后實(shí)際上不完全是——初步測試彩倚,http,https,ftp等 scheme 其實(shí)是完全沒問題,包括設(shè)置 session 級別的 Cookie扶平;在此之外帆离,設(shè)置自定義 scheme 的 session Cookie 會 fail,持久化 Cookie 是可行的结澄。 沒想到吧

- 自定義 scheme 哥谷,可以發(fā) post 請求嗎?

是的麻献。在 webkit 實(shí)現(xiàn)有 bug 的情況下们妥,可以用 form 發(fā)送 post 請求。不僅如此勉吻,使用 form post监婶,可以成功將 body 發(fā)送到 native,這個(gè)就厲害了餐曼。

- 自定義 scheme 压储,可以攔截 xhr 請求?是否丟失 POST 請求的 body源譬?

事實(shí)上是可以攔截的集惋。在上面表格里雖然 http、https 下都是 ?踩娘,但是那是因?yàn)?domain 是標(biāo)準(zhǔn)協(xié)議刮刑。如果是

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

那么 xhr 依然也可以發(fā)送出去喉祭,但是不幸的時(shí),xhr 里 body 還是丟了雷绢。

3 實(shí)踐

有了上述的前提泛烙。我們有一個(gè)簡單的離線包渲染的方案。

  1. 把加載頁面的 domain 改為 自定義協(xié)議如 apphost翘紊;
[self.webView loadHTMLString:html baseURL:[NSURL URLWithString:@"apphost://test.com/index.2.html"]];
  1. 加載的 HTML 文件應(yīng)用的資源全部用://user/a.png這種相對的寫法蔽氨。這樣 http 和 自定義協(xié)議都可以用。
  2. HTML 里面 ajax 請求使用絕對路徑帆疟,修改后端的 Response 頭鹉究,使用 Cross-Origin Resource Sharing (CORS) 技術(shù),配合對 ajax 請求追加幾個(gè)參數(shù)踪宠,也可以實(shí)現(xiàn)帶上 Cookie 的請求

上面的做法自赔,可以攔截基本上傳統(tǒng) UIWebview 時(shí)代可攔截的所有類型。但是缺點(diǎn)也很明顯柳琢。

  • iOS 11+ 以上才能用
  • https 下支持有限
  • 對 xhr js 代碼小幅度的改動(dòng)
  • 最最嚴(yán)重的是绍妨,不支持 session 級別的 Cookie 設(shè)置。

在 AppHost 框架里柬脸,作者沒有采用這種方式他去,而是采用了讀 html,解析靜態(tài)資源的方式來實(shí)現(xiàn)加載離線包的功能肖粮,同時(shí)不影響 session 級別的 Cookie 設(shè)置孤页。

總的來說 WKURLSchemeHandler 的使用場景還是比較有限,不能攔截 http 請求涩馆,有些結(jié)合 NSURLSession行施,先下載 html 的方式,然后再代理的方式魂那,對 webview 的加載速度反而是拖累蛾号,還需要想想其它辦法。

結(jié)論:WKURLSchemeHandler 最好的場景還是用在加載本地圖片上涯雅,其它方面不穩(wěn)定鲜结,慎用。

4. 注

  1. AppHost.framework 是作者在網(wǎng)易有錢活逆、嚴(yán)選工作中精刷,抽離出來 JSBridge 的庫,實(shí)現(xiàn) native 和 h5 之間的通訊蔗候,內(nèi)置諸多常用的功能怒允,在業(yè)務(wù)簡單的情況下可開箱即用,復(fù)雜情況允許靈活定制锈遥。預(yù)計(jì)在4月底開源

5.參考

  1. network-scheme 的定義
  2. RFC6265里對 Cookie domain 的說明
  3. Preflighted requests
  4. 關(guān)于 ajax 跨域攜帶 Cookie
  5. 測試 demo 源碼
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纫事,一起剝皮案震驚了整個(gè)濱河市勘畔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丽惶,老刑警劉巖炫七,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異钾唬,居然都是意外死亡万哪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門抡秆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來壤圃,“玉大人,你說我怎么就攤上這事琅轧。” “怎么了踊挠?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵乍桂,是天一觀的道長。 經(jīng)常有香客問我效床,道長睹酌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任剩檀,我火速辦了婚禮憋沿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沪猴。我一直安慰自己辐啄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布运嗜。 她就那樣靜靜地躺著壶辜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪担租。 梳的紋絲不亂的頭發(fā)上砸民,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音奋救,去河邊找鬼岭参。 笑死,一個(gè)胖子當(dāng)著我的面吹牛尝艘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播利耍,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼盔粹,長吁一口氣:“原來是場噩夢啊……” “哼程癌!你這毒婦竟也來了舷嗡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤嵌莉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后锐峭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沿癞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年援雇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片椎扬。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蚕涤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情揖铜,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布贿肩,位于F島的核電站失仁,受9級特大地震影響尸曼,放射性物質(zhì)發(fā)生泄漏萄焦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一拂封、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冒签,春花似錦、人聲如沸刚梭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至衅金,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間氮唯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工豆励, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瞒渠。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓肆糕,卻偏偏與公主長得像在孝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子私沮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容