最近對前后端分離設計模式的理解總結:
為什么要做前后端分離:
-
有些人會說:因為職責明確,因為不再用模板做視圖層 render HTML,后端不用自己寫前端就會輕松些... 這些大多說的不錯,但還是比較表象,主要的原因其實是 源于需求變化:
在現代web開發中,我們對于前端的需求越來越復雜,我們需要 PC端瀏覽器、手機端瀏覽器 、Android APP 和 iPhone APP 、微信小程序等多種需求。
由于我們前端可能涉及設計到的頁面種類越來越多,而 vue、angular、react 等前端框架越來越好的支持:“ Code once,run everywhere. ”,所以前端的開發漸漸獨立、并更為體系化。
故而曾經那種單憑一套模板渲染頁面的方法似乎太局限了。
而一旦采用 前后端分離 的模式,并且采用 restful 設計規范,那么后端就只需要提供數據 API 接口即可,后端只需要長期維護這一套代碼就行了。
Django 的 FBV & CBV
FBV:Function base view 基于方法的視圖
CBV:Class base view 基于類的視圖
在我最開始嘗試用 Flask 框架遵循 Restful 規范按路由設計接口時,常常有這樣幾個疑問和思考:
- 我的需求這么多,難道沒一個都要設計一個接口嗎?這樣方便維護嗎?
- 比如一個接口可能有 GET、POST 或是 PUT 等類型的請求,我難道每次都要寫一個選擇分支:if request.method == ' ... ': 才可以嗎?
所以之后我了解到,其實是我沒有了解 FBV 與 CBV 的概念。
正所謂:類就是 把數據封裝進對象里 ,并賦予對象 行為 的能力。
所以我們完全可以把一個需求的接口封裝成為一個類:
from django.views import View
class UserView(View): # 我們假設這是一個關于用戶的接口,django/Flask中路由也叫視圖。
def get(self, request, *args, **kwargs):
# ... 對應GET請求
def post(self, request, *args, **kwargs):
# ... 對應POST請求
def put(self, request, *args, **kwargs):
# ... 對應PUT請求
def delete(self, request, *args, **kwargs):
# ... 對應DELETE請求
因為繼承了 django 的 View 類,所以在默認情況下,會自動根據請求類型映射該類中對應的請求方法。
但是在所有的 python web 框架乃至一些其他語言的框架之中,對 HTTP 請求類型的方法映射都是由一個專門的反射函數來實現的。
# 本身在django的源碼 base.py 中:
def dispatch(self, request, *args, **kwargs):
# 是有這樣一個基礎的反射方法的。
# Override (但在我們繼承了 View 的類中,我們可以重載它試驗一下)
from django.views import View
class User(View):
def dispatch(self, request, *args, **kwargs):
func = getattr(self, request.method.lower()) #必須要小寫化不然映射不到我們剛才寫的四個對應方法
ret = func(self, request, *args, **kwargs)
return ret
# ...下面四個方法同上
所以,總結如下:
-
CBV:基于反射實現根據請求方式不同,執行不同的方法。
原理:URL -> view方法 -> dispatch方法(反射執行其他:GET/POST/DELETE/PUT)
另外值得一提的是:自己那個類中的 dispatch 方法中如果不自己去映射而是調用父類(django 的 View)的 dispatch 方法,另外還在前后做一些附加操作,這樣的功能跟 “裝飾器” 就很相似了。
RESTful API 設計規范
RESTful 規范是一種建議而非硬性要求,但是在前后端分離是大趨勢的當下,越來越多的程序員們開始喜歡遵照 RESTful 規范來設計后端接口。
一共有10個項目,那讓我們一起來慢慢學習吧!
-
API與用戶的通信協議,總是使用HTTPS協議。
- 因為HTTPS比HTTP更加安全,所以在建立、部署大中型網站時企業都會選擇使用HTTPS,但是由于HTTPS需要配置專門的證書,而如果需要具備較高的認可度的證書,則需要從證書商處購買,而價格大多不便宜。
-
域名配置要體現自己是個 API:
-
子域名形式:https://api.example.com ( 存在跨域的問題 )
引用他人的 api接口 也得在自己的請求處解決跨域問題
-
-
后端API接口請區分版本
-
一般都是加在 URL 上,其他的一般業內很少用:
-
-
路徑 -> 面向資源編程:
何為面向資源編程呢?就是把互聯網上的任何東西都視為一種資源,而我們在后端對該實體描述的 API 路由都為名詞。
而不是類似于 getUser、addUser 這樣的動詞!
具體請看下一條 ...
-
methods :HTTP方法
始終記住 RESTful 設計要充分利用 HTTP協議里的 GET、POST、PUT、PATCH 和 DELETE 等 方法標志 來表達對數據的 增刪改查。
- GET /collection:返回資源對象的列表(數組)
- GET /collection/resource:返回單個資源對象
- POST /collection:返回新生成的資源對象
- PUT /collection/resource:返回完整的資源對象
- PATCH /collection/resource:返回完整的資源對象
- DELETE /collection/resource:返回一個空文檔
-
對資源的條件過濾 -> 用 URL傳參 來指定過濾條件
類似于 例如查訂單時 http://api.mall.com/orders?sortby=dectime 則是以按降序時間排列訂單...
-
一定要使用 狀態碼:
常見的狀態碼:
200 系列: 成功及其附屬狀態信息
- 200 OK - [GET]:服務器成功返回用戶請求的數據,該操作是冪等的(Idempotent)。
- 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。
- 202 Accepted - [*]:表示一個請求已經進入后臺排隊(異步任務)
- 204 NO CONTENT - [DELETE]:用戶刪除數據成功。
- ...
300 系列:重定向類
- 例如 301 MOVED PERMANENTLY
- ...
400 系列 :客戶端錯誤
- 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操作,該操作是冪等的。
- 401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。
- 403 Forbidden - [*] 表示用戶得到授權(與401錯誤相對),但是訪問是被禁止的。
- 404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操作,該操作是冪等的。
- 406 Not Acceptable - [GET]:用戶請求的格式不可得(比如用戶請求JSON格式,但是只有XML格式)。
- 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再得到的。
- 422 Unprocessable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤。
- ...
500 系列: 服務端錯誤
- 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將無法判斷發出的請求是否成功。
- ...
單光用 HTTP狀態碼 在實際開發中是遠遠不夠的哦!
一般還會加上一個我們自己的 code;
參見Alibaba Alipay支付寶文檔:https://docs.open.alipay.com/common/105806 code( 返回碼 )
這些自定義的返回碼大大增加了我們能夠表示的 服務對客戶端請求的響應狀態的類型。
由于狀態碼十分有限,所以一般前端大多會被要求處理 自定義的這個code。
-
錯誤處理
承接上一個小段,當狀態碼是 4xx 時,應當返回錯誤信息,error 當做 key:
{ error:"Invalid API key!" }
-
以 API url 來確定返回類型!
- GET /collection:返回資源對象的列表(數組)
- GET /collection/resource:返回單個資源對象
- POST /collection:返回新生成的資源對象
- PUT /collection/resource:返回完整的資源對象
- PATCH /collection/resource:返回完整的資源對象
- DELETE /collection/resource:返回一個空文檔
這樣干巴巴地說自然很難理解那個 collection 是什么意思:還是以訂單舉例:
http://api.mall.com/orders/18924442 => orders 是復數(一般來說,數據庫中的表都是同種記錄的"集合"(collection),所以API中的名詞也應該使用復數。),我的操作目標是 訂單集合,而后面的 18924442(一個訂單 id 示例)就代表著資源對象的實體 resource!
那么我對這個 api url 的 post 、delete 、put / patch 和 get 就分別對應了該資源的 增刪改查 接口。
-
HypermediaAPI:
即返回結果中提供鏈接,連向其他API方法,使得用戶不查文檔,也知道下一步應該做什么。
比如:對于我正準備要實現的 RPZ計協 · 科丁特沃夫咖啡廳論壇 桌帖的評論列表 這件事情:
/coffeeComments?postid=12 第十二個咖啡桌帖的評論列表: [ { id: '100030001', author: '飛翔的海貓', content: '你這個寫的有bug呀!' datetime: '2019-01-19 22:10:35', likes: 0 commentsList: 'https://api.sicnurpz.online/coffeeComments?commentid=100030001' } ]
-
前面的都是 評論數據類型該有的基本信息比如 評論者的昵稱、評論內容等,但為什么我要在最后加上一個 commentsList,還是個 url 地址字符串呢?
因為這樣我就不用再在服務端去拼接 查詢該條評論的評論列表 的字符串了! 提高了后端接口處理的效率,在現代服務器存儲量充足的情況下,數據存儲空間 換 網絡響應時間自然是不虧的啦!