先後端分離實踐

先後端分離並非什麼新鮮事,處處都是先後端分離的實踐。然而一些歷史項目在從一體化 Web 設計轉向先後端分離的架構時,仍然不可避免的會遇到各類各樣的問題。因爲層出不窮的問題,甚至會有團隊質疑,一體化好好的,爲何要先後端分離?前端

說到底,並非先後分離很差,只是可能不適合,或者說……設計思惟尚未轉變過來……算法

clipboard.png

一體式 Web 架構示意數據庫


clipboard.png

先後分離式 Web 架構示意json

爲何要先後端分離

比爲何要先後端分離更現實的問題是何時須要先後端分離,即先後端分離的應用場景。segmentfault

提及這個問題,我想到了 2011 年左右,公司在以 .NET 開發團隊爲主的基礎上擴展了 Java 團隊,兩個團隊雖然是在作不一樣的產品,可是仍然存在大量重複性的開發,好比用 ASP.NET WebPage 寫了組織機構相關的頁面,用 JSP 又要再寫一遍。在這種狀況下,團隊就開始思考這樣一個方案:若是前端實現與後端技術無關,那頁面呈現的部分就能夠共用,不一樣的後端技術只須要實現後端業務邏輯就好。後端

方案根本要解決的問題是把數據和頁面剝離開來。應對這種需求的技術是現成的,前端採用靜態網頁相關的技術,HTML + CSS + JavaScript,經過 AJAX 技術調用後端提供的業務接口。先後端協商好接口方式經過 HTTP 提供,統一使用 POST 謂詞。接口數據結構使用 XML 實現,前端 jQuery 解析 XML 很方便,後端對 XML 的處理工具就更多了……後來因爲後端 JSON庫(好比 Newtonsoft JSON.NET、jackson、Gson 等)崛起,前端處理 JSON 也更容易(JSON.parse()JSON.stringify()),就將數據結構換成了 JSON 實現。api

這種架構從本質上來講就是 SOA(面向服務的架構)。當後端不提供頁面,只是純粹的經過 Web API 來提供數據和業務交互能力以後,Web 前端就成了純粹的客戶端角色,與 WinForm、移動終端應用屬於一樣的角色,能夠把它們合在一塊兒,統稱爲前端。之前的一體化架構須要定製頁面來實現 Web 應用,同時又定義一套 WebService/WSDL 來對 WinForm 和移動終端提供服務。轉換爲新的架構以後,能夠統一使用 Web API 形式爲全部類型的前端提供服務。至於某些類型的前端對這個 Web API 進行的 RPC 封裝,那又是另一回事了。安全

經過這樣的架構改造,先後端實際就已經分離開了。拋開其它類型的前端不提,這裏只討論 Web 前端和後端。因爲分離,Web 前端在開發的時候壓根不須要了解後端是用的什麼技術,只須要後端提供了什麼樣的接口能夠用來作什麼事情就好,什麼 C#/ASP.NET、Java/JEE、數據庫……這些技術能夠通通不去了解。然後端的 .NET 團隊和 Java 團隊也脫離了邏輯無關的美學思惟,不須要面對美工精細的界面設計約束,也不須要在思考邏輯實現的同時還要去考慮頁面上怎麼佈局的問題,只須要處理本身擅長的邏輯和數據就好。前端框架

先後端分離以後,兩端的開發人員都輕鬆很多,因爲技術和業務都更專一,開發效率也提升了。分離帶來的好處漸漸體現出來:服務器

1. 先後職責分離

前端傾向於呈現,着重處理用戶體驗相關的問題;後端則傾處於業務邏輯、數據處理和持久化等。在設計清晰的狀況下,後端只須要以數據爲中心對業務處理算法負責,並按約定爲前端提供 API 接口;而前端使用這些接口對用戶體驗負責。

2. 先後技術分離

前端能夠不用瞭解後端技術,也不關心後端具體用什麼技術來實現,只須要會 HTML/CSS/JavaScript 就能入手;然後端只須要關心後端開發技術,除了省去學習前端技術的麻煩,連 Web 框架的學習研究都只須要關注 Web API 就好,而不用去關注基於頁面視圖的 MVC 技術(並非說不須要 MVC,Web API 的接口部分的數據結構呈現也是 View),不用考慮特別複雜的數據組織和呈現。

3. 先後分離帶來了用戶用戶體驗和業務處理解耦

前端能夠根據用戶不一樣時期的體驗需求迅速改版,後端對此毫無壓力。同理,後端進行的業務邏輯升級,數據持久方案變動,只要不影響到接口,前端能夠絕不知情。固然若是需求變動引發接口變化的時候,先後端又須要坐在一塊兒同步信息了。

4. 先後分離,能夠分別歸約兩端的設計

後端只提供 API 服務,不考慮頁面呈現的問題。實現 SOA 架構的 API 能夠服務於各類前端,而不只僅是 Web 前端,能夠作到一套服務,各端使用;同時對於前端來講,不依賴後端技術的前端部分能夠獨立部署,也能夠應於 Hybrid 架構,嵌入各類「殼」(好比 Electron、Codorva 等),迅速實現多終端。

先後分離架構

任何技術方案都不是銀彈,先後分離不只帶來好處,也帶來矛盾。咱們在實踐初期,因爲前端團隊力量相對薄弱,同時按照慣例,全部業務處理幾乎都是由後端(原來的技術骨幹)來設計和定義的,前端處理過程當中經常發現接口定義不符合用戶操做流程,AJAX 異步請求過多等問題。畢竟後端思惟和前端思惟仍是有所不一樣——前端思惟傾向於用戶體驗,然後端思惟則更傾向於業務的技術實現。

除此以外,先後分離在安全性上的要求也略有不一樣。因爲先後分離本質上是一種 SOA 架構,因此在受權上也須要按 SOA 架構的方式來思考。Cookie/Session 的方式雖然可用,但並非特別合適,相對來講,基於 Token 的認證則更適合一些。採用基於 Token 的認證就意味着後端的認證部分須要重寫……後端固然不想重寫,因而會將皮球踢給前端來讓前端想辦法實現基於 Cookie/Session 的認證……因而前端開始報怨(悲劇)……

誰來主導

這些矛盾的出現,歸根結底在於設計不夠清晰明確。毫無疑問,在開發過程當中,主導者應該是架構師或者設計師。然而實際場景中,架構師或者設計師每每也是開發人員,因此他們的主要技術棧會極大的影響先後端在整個項目中的主次做用。這位骨幹處於哪端,開發的便捷性就會向哪端傾斜。這是一個很差的現象,可是咱們不得不面對這樣的現狀,我相信不少不太大的團隊也面臨着相似的問題。

若是沒有良好的流程規範,一般前端接觸的到角色會比後端更多(多數應用型項目/產品,並不是全部狀況)。

  • 前端開發人員會受到項目/產品經理或客戶的直接影響:這個地方應該放個按鈕,那個操做應該這麼進行……;
  • 前端還要與美工對接——這樣的設計很差實現,是否能夠改爲那樣?客戶要求必須這麼操做,可是這個設計作不到;
  • 前端還要跟後端對接,對於某些應用,甚至是多個後端

換句話說,前端能夠成爲項目溝通的中心,因此比後端更合適承擔主導的角色。

接口設計

接口分後端服務實現和前端調用兩個部分,技術都是成熟技術,並不難,接口設計纔是難點。前面提到先後端會產生一些矛盾。從前端的角度來看,重點關注的是用戶體驗,包括用戶在進行業務操做時的流動方向和相關處理;而從後端的角度來看,重點關注的是數據完整、有效、安全。矛盾在於雙方關注點不一樣,信息不對稱,還各有私心。解決這些矛盾的着眼點就是接口設計。

接口設計時,其粒度的大小每每表明了先後端工做量的大小(非絕對,這和總體架構有關)。接口粒度過小,前端要處理的事情就多,尤爲是對各類異步處理就可能會感到目不暇接;粒度太大,就會出現高耦合,下降靈活性和擴展性,固然這種狀況下後端的工做就輕鬆不了。業務層面的東西涉及到具體的產品,這裏很少作討論。這裏主要討論一點點技術層面的東西。

就形式上來講,Web API 能夠定義成 REST,也能夠是 RPC,只要先後端商議肯定下來就行。更重要的是在輸入參數和輸出結果上,最好一開始就有相對固定的定義,這每每取決於前端架構或採用的 UI 框架。

常見請求參數的數據形式有以下一些:

  • 鍵值對,用於 URL 中的 QueryString 或者 POST 等方法的 Payload
  • XML/JSON/...,一般用於 POST 等方法的 Payload,也可使用 multipart 傳遞
  • ROUTE,由後端路由解析 URL 取得,在 RESTful 中經常使用

而服務器響應的數據形式就五花八門各式各樣了,一般一個完整的響應至少須要包含狀態碼、消息、數據三個部分的內容,其中

  • 狀態碼,HTTP 狀態碼或響應數據中特定的狀態屬性
  • 消息,一般是放在響應內容中,做爲數據的一部分
  • 數據,根據接口協議,多是各類格式,當前最流行的是 JSON

咱們在實踐中使用 JSON 形式,最初定義了這樣一種形式

{
    "code": "number",
    "message": "string",
    "data": "any"
}

code 主要用於指導前端進行一些特殊的操做,好比 0 表示 API 調用成功,非0 表示調用失敗,其中 1 表示須要登陸、2 表示未獲取受權……對於這個定義,前端拿到響應以後,就能夠在應用框架層進行一些常規處理,好比當 code1 的時候,彈出登陸窗口請用戶在當前頁面登陸,而當 code2 的時候,則彈出消息提示並後附連接引導用戶獲取受權。

參閱: 先後分離模型之封裝 Api 調用

一開始這樣作並無什麼問題,直到前端框架換用了 jQuery EasyUI。以 EasyUI 爲例的好多 UI 庫都支持爲組件配置數據 URL,它會自動經過 AJAX 來獲取數據,但對數據結構有要求。若是仍然採用以前設計的響應結構,就須要爲組件定義數據過濾器(filter)來處理響應結果,這樣作寫 filter 以及爲組件聲明 filter 的工做量也是不小的。爲了減小這部分工做量咱們決定改一改接口。

新的接口是一種可變結構,正常狀況下返回 UI 須要的數據結構,出錯的狀況則響應一個類型於原定結構的數據結構:

{
    "error": {
        "identity": "special identity string",
        "code": "number",
        "message": "string",
        "data": "any"
    }
}

對於新響應數據結構,前端框架只須要判斷一下是否存在 error 屬性,若是存在,檢查其 identity 屬性是否爲指定的特殊值(好比某個特定的 GUID),而後再使用其 codemessage 屬性處理錯誤。這個錯誤判斷過程略爲複雜一些,但能夠由前端應用框架統一處理。

若是使用 RESTful 風格的接口,部分狀態碼能夠用 HTTP 狀態碼代替,好比 401 表示須要登陸,403 就能夠表示沒有得到受權,500 表示程序處理過程當中發生錯誤。固然,雖然 HTTP 狀態碼與 RESTful 風格更配,可是非 RESTful 風格也可使用 HTTP 狀態碼來代替 error.code

用戶認證

認證方案不少,好比 Cookie/Session 在某些環境下仍然可行、也可使用基於 Token 和 OAuth 或者 JWT,甚至是本身實現基於 Token 的認證方式。

基於 Cookie/Session 的認證方案

採用傳統的 Cookie/Session 認證方案並不是不可行,只不過有一些限制。若是前端部分和後端部分同源,好比頁面發佈在 http://domain.name/,而 Web API 發佈在 http://domain.name/api/,這種狀況下,原來的一體式 Web 方案所採用的 Cookie/Session 方案能夠直接遷移過來,毫無壓力。可是若是前面發佈和 API 發佈不一樣源,這種方法處理起來就複雜了。

而後通常先後端分離的開發方式,無論是開發階段仍是發佈階段,不一樣源的可能性佔絕大比例,因此認證方案一般會使用與 Cookie 無關的方案。

基於 OAuth 的認證方案

目前各大網站的開放式接口都是 SOA 架構,若是把這些開放式接口看做提供服務方(服務端),而把使用這些開放式接口的應用看做客戶端,那麼就能夠產生這樣一種和先後分離對應的關係:

前端 ⇌ 客戶端
     ⇣
   基於 OAuth 的認證)
     ⇡ 
後端 ⇌ 服務端

因此,開放式接口普遍使用的 OAuth 方案用於先後分離是可行的,但在具體實施上卻並非那麼容易。尤爲是在安全性上,因爲前端是徹底暴露在外的,與 OAuth 一般實施的環境(後端⇌服務端)相比,要注意的是首次認證不是使用已註冊的 AppID 和 AppToken,而是使用用戶名和密碼。

基於 Token/JWT 的認證方案

雖然這個方案放在最後,但這個方案倒是目前先後端分離最適合的方案。基於 Token 的認證方案,各類討論由來已久,而 JWT 是相對較爲成熟,也獲得多數人承認的一種。從 jwt.io 上能夠找到各類技術棧的 JWT 實現,應用起來也比較方便。

話雖如此,JWT 方案和之前使用的 Cookie/Session 在處理上仍是有較大的差異,須要必定的學習成本。有人擔憂 JWT 的數據量太大。這確實是一個問題,可是硬件並不貴,4G 也開始進入不限流量階段,通常應用中不用太在乎這個問題。

先後分離的測試

先後分離以後,前端的測試將以用戶體驗測試和集成測試爲主,然後端則主要是進行單元測試和 Web API 接口測試。與一體化的 Web 應用相比,多了一層接口測試,這一層測試能夠徹底自動化,一旦完成測試開發,就能在很大程度上控制住業務處理和數據錯誤。這樣一來,集成測試的工做量會相對單一也容易得多。

前端測試的工做相對來講減輕不了多少,先後分離以後的前端部分承擔了原來的集成測試工做。可是在假設 Web API 正確的狀況下進行集成測試,工做量是能夠減輕很多的,用例能夠只關注前端體驗性的問題,好比呈現是否正確,跳轉是否正確,用戶的操做步驟是否符合要求以及提示信息是否準確等等。

對於用戶輸入有效性驗證這部分工做在項目時間緊迫的狀況下甚至均可以徹底拋給 Web API 去處理。無論是否先後端分離,Web 開發中都有一個共識:永遠不要相信前端!既而後端必須保證數據的安全性和有效性,那麼前端省略這一步驟並不會對後端形成什麼實質性的威脅,最多隻是用戶體驗差一點。可是,若是先後端都要作數據有效性驗證,那必定要嚴格按照文檔來進行,否則很容易出現先後端數據驗證不一致的狀況(這不是先後分離的問題,一體化架構一樣存在這個問題)。

小結

總的來講,先後分離所帶來的好處仍是很明顯的。可是具體實施的時候須要一個全新的思考方式,而不是基於原有一體化 Web 開發方式來進行思考。先後分離的開放方式將開發人員從複雜的技術組合中解放出來,你們均可以更專一於本身擅長的領域來進行開發,但同時也對先後端團隊的溝通交流提出了更高的要求,先後端團隊必需要一同設計出相對穩定的 Web API 接口(這部分工做其實無論是否先後端分離都是少不了的,只是先後分離的架構對此要求更高,更明確地要求接口不僅存在於人的記憶中,更要文檔化、持久化)。

相關文章
相關標籤/搜索