基於 HTTP 請求攔截,快速解決跨域和代理 Mock

近幾年,隨着 Web 開發逐漸成熟,先後端分離的架構設計愈來愈被衆多開發者承認,使得前端和後端能夠專一各自的職能,下降溝通成本,提升開發效率。前端

在先後端分離的開發模式下,前端和後端工程師得以並行工做。當遇到前端界面展現須要的數據,然後端對應的接口尚未完成開發的狀況時,須要一個數據源來保證前端工做的順利進行。webpack

今天這篇文章,咱們會介紹幾種常見的方法和其中存在的問題,並提出如何基於HTTP 請求攔截,快速解決跨域和代理 mock 問題的方案。ios

常見方法及問題

請求 mock 服務器

最常規的作法是維護一個提供靜態數據的 mock 服務器(它提供的數據稱爲 mock 數據),前端請求 mock 服務器獲取數據便可,但這種靜態數據維護不便。web

請求 AMP

更好的作法是有一個根據接口定義來自動生成數據的 mock 服務器,咱們稱爲AMP(接口管理平臺,API Manage Platform),前端請求該服務器獲取數據。後端

在這種場景下,若是有些接口已經完成開發,前端須要手動修改代碼去設置不一樣接口的請求地址。當接口數量較多時,這種方法會變得很是低效。所以, AMP 通常也會同時提供代理功能,也就是指前端仍請求 AMP,AMP 會根據接口完成狀況來決定返回 mock 數據,仍是將請求再次代理到真實的業務服務器獲取數據後返回。跨域

可是這種方案的問題在於當涉及到須要角色權限驗證的接口時,登陸輸入用戶信息後在瀏覽器中會緩存 cookie,當訪問與登陸時同域名的接口時,瀏覽器會自動攜帶 cookie,由服務器解析 cookie 並鑑權後獲取對應權限的接口數據。前端通常是在本地啓動服務器進行開發,當業務服務器的接口完成開發,這時再採用請求 AMP 的方法切換接口數據,就會出現跨域的狀況。瀏覽器

因爲瀏覽器的安全機制決定跨域訪問時沒法攜帶 cookie,而且沒法經過代碼讀取 cookie,所以經過代碼傳遞 cookie 跨域不可行,而現有的解決方案也不完美:緩存

  • 若是在 AMP 額外增長模擬登錄的功能,會由於全部接口的權限固定不變,沒法適配一個接口對不一樣角色有不一樣權限而返回相應的數據;並且一旦鑑權的接口功能變動、失效等狀況發生,都須要重寫修改 AMP 的代理功能,代價較大。安全

  • 若是利用瀏覽器插件保存登錄信息、提供代理,則須要兼容不一樣瀏覽器,成本過高。服務器

針對上述技術問題,本文提出了一種可跨瀏覽器,並在前端實現的不侵入業務代碼的代理方法。

基於 HTTP 請求攔截

實現前端接口代理

基於 HTTP 請求攔截實現前端接口的方式,從更底層的角度實現了接口開發完成先後的 mock 數據,及業務服務器真實數據之間的切換,而且解決了現有技術中由 HTTP 請求經過 AMP 代理到業務服務器產生跨域沒法攜帶權限信息,致使沒法按照角色權限返回請求數據的技術問題。

主要創新點

  1. 在更底層基於 XMLHttpRequest 和 Fetch API 實現攔截代理,不須要考慮主流瀏覽器類型,和 JavaScript 依賴的工具庫;

  2. 在前端實現代理,保留了登錄信息,無需額外處理鑑權問題;

  3. 提供一種能夠快速實現且可插拔的使用方式。

總的來講,這個方案提供了一種可快速實現,運行在前端瀏覽器中,且不依賴瀏覽器類型的請求代理方法。

設計思路

Web 前端開發通常使用 JavaScript 語言,瀏覽器環境的 HTTP 請求都是基於 Fetch API 或 XMLHttpRequest API 來實現的(基於前者的請求記作 xhr,後者記作 fetch),主流的 Javascript 開源工具庫如 Axios、Request 也是這樣。因此,咱們的方案就是要經過在底層攔截 xhr 或 fetch,根據必定的判斷邏輯來實現前端代理功能。

實現方式

首先,從新封裝瀏覽器環境中原生的 XMLHttpRequest API 和 Fetch API。基本思路是將這兩個原生的 API 保存起來,添加到各自從新封裝的同名 API 中(記做新 API),爲新 API 寫入與原生 API 中同名的方法和屬性,在攜帶請求參數的同名方法(好比下文中的 open 和 send)里加入攔截請求和代理的邏輯 ApiProxy,對外開放一個可配置該攔截邏輯的接口,用於配置針對不一樣的 HTTP 請求格式所請求數據的攔截和代理邏輯。

圖1:代理與AMP和終端業務的交互流程

ApiProxy 在這個過程當中的主要做用和工做流程能夠概括爲

  1. 註冊攔截器。接收並攔截 HTTP 請求,解析該請求中的參數,這裏的參數是指能在 AMP 中惟一標識該接口的參數,好比域名+請求方法(如 GET、POST 等)+路徑(如 https://service.com/user 中的/user)。

  2. 根據該參數生成發送 AMP 的請求。AMP 實時維護了 mock 服務器上存儲的接口以及業務服務器上存儲的真實接口的相關信息,包括接口的定義、域名、屬性、開發狀態等。

  3. AMP 根據請求查詢接口定義數據,若是接口存在且狀態是開發中,則返回根據接口定義生成的 mock 數據,不然返回特定響應標誌,如圖 1 中的「{code:』200302』}」。

  4. Apiproxy 收到 AMP 的響應後判斷是否有特殊標誌,沒有直接返回 mock 數據到原請求,有則表示後端接口開發完成,繼續發送原 HTTP 請求到後端服務器請求後端服務器存儲的真實數據,至關於沒有對原請求作任何處理。

和傳統的將 HTTP 請求發送給 AMP 不一樣的是 ,AMP 根據接口狀態判斷是根據請求直接返回 mock 數據,仍是開啓代理將 HTTP 請求再發送給業務服務器(此時跨域訪問會丟失原始 HTTP 請求中瀏覽器攜帶的 cookie),不直接將 HTTP 請求發送給 AMP,而是對請求正式發出以前進行攔截,並解析其中的參數發送給 AMP,由 AMP 反饋接口狀態,若開發完成則將 HTTP 請求正式發送給業務服務器。由於沒有修改該請求,只是延遲發送,這樣就保持了原請求與業務服務器之間的全部鑑權等相關信息,由此解決了跨域訪問沒法攜帶 cookie 的問題。

不一樣請求方式下 ApiProxy 的實現

因爲不一樣請求方式的底層設計不一樣,咱們相應的具體封裝手段也不一樣。

圖2:代理核心工做原理

XMLHttpRequest

對於 XMLHttpRequest 請求,在其 open 方法中解析請求,訪問 AMP 根據響應結果判斷是否須要繼續發送原請求到後臺服務器,一個 xhr 只有在其 send 方法被調用時纔會真正的發起 HTTP 請求,而在 open 方法中沒法獲取到 send 方法傳遞的數據,因此攔截髮生在 send 方法中。首先單獨存儲 send 方法中發送請求時的參數,而後直接返回,確保先不調用真正的 XMLHttpRequest 的 send 方法,將單獨存儲的參數生成對 AMP 的請求,執行上述 AMP 中的判斷。

實例

一、定義與原生 XMLHttpRequest API 同名的接口,稱爲新的 XHR 接口;

二、重命名原生 XMLHttpRequest API 並添加到新的 XHR 接口;

三、在新的 XHR 接口中定義與原生 XMLHttpRequest API 同名的屬性和方法;

四、在同名的 open 方法中解析 HTTP 請求,獲得用來在 AMP 查詢接口狀態的參數(好比域名+請求方法+路徑);

五、攔截將要發送的原請求,在同名的 send 方法中暫存原請求要發送的數據,暫停原請求的發送;

六、用 4 中的參數請求 AMP,查詢接口狀態,若是接口不存在或是已完成狀態,則返回特殊標誌,ApiProxy 取出 5 中暫存的數據,傳遞給原請求,並繼續原請求的發送;不然,AMP 返回 mock 數據,ApiProxy 直接將該數據返回給原請求。

Fetch API

對於 Fetch API 而言,由於它是基於 Promise 實現的,攔截比較容易,只須要在 Fetch API 外層封裝一個 Promise 入口,在其發起 fetch 請求前,先暫停原請求,解析數據請求 AMP,並等待響應,判斷響應是否有特殊響應碼,若是有則繼續原請求,不然跳過原請求,直接返回 mock 數據。

啓動前端代理功能

在前端實際開發中,能夠藉助打包工具,好比 webpack,自定義一個可配置的插件,開啓後在開發環境中自動將代理攔截代碼插入到主頁面裏,從而啓動前端代理功能。

小結

本文提出的前端代理方法經過將代理職責下沉到前端,減小了 mock 服務器(或者接口管理平臺)請求真實業務服務器步驟,同時將角色權限保持在前端請求中,進一步減小了代理所須要承擔的工做量,從底層攔截 HTTP 請求的方法,繞過了利用瀏覽器插件作代理帶來的瀏覽器兼容的問題。最後提供的利用打包工具(如 webpack)封裝這種代理方法,實現快速插拔的前端代理。

本文做者奴止,馬蜂窩社區研發團隊前端開發工程師,主要負責社區管理後臺,接口管理平臺開發等工做。

關注馬蜂窩技術,找到更多你須要的內容

附:參考資料

關於跨域:

developer.mozilla.org/en-US/docs/…

關於XMLHTTPRequest:

developer.mozilla.org/en-US/docs/…

關於Fetch:

[https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API

相關文章
相關標籤/搜索