【全棧修鍊】422- RESTful 架構及實踐 修鍊寶典
- 2019 年 11 月 27 日
- 筆記
一、概念介紹
1. REST 概念
REST:(Representational State Transfer)即表現層狀態轉換,定義了資源的通用訪問格式,是一種網路應用程式的設計風格和開發方式。
在概念中,需要理解以下幾個名稱:
- 資源(Resource)
即伺服器上獲取到的東西任何資源,一條用戶記錄,一個用戶的密碼,一張圖片等等都是。
- 資源的表述(Representation)
即資源格式,是 HTML、XML、JSON、純文本、圖片等等,可以用各種各樣的格式來表述你獲取到的資源。
- 狀態轉移(State Transfer)
即URL定位資源,用 HTTP 動詞(GET,POST,DELETE,DETC)描述操作。操作是動詞,資源是名詞。
- 統一介面(Uniform Interface)
即通過統一的介面對資源進行操作。
2. REST 特點
REST 通常基於使用HTTP
,URI
,和XML
以及HTML
這些現有的廣泛流行的協議和標準,每一種 URI 代表一種資源。
REST 通常使用JSON
數據格式。
REST 基本架構的四個方法:
- GET– 用於獲取數據
- PUT– 用於更新或添加數據
- DELETE– 用於刪除數據
- POST– 用於添加數據
下面會通過一個場景介紹。
3. REST 優點
- 更高效利用快取來提高響應速度。
- 讓不同的伺服器的處理一系列請求中的不同請求,提高伺服器的擴展性
- 瀏覽器即可作為客戶端,簡化軟體需求。
- 相對於其他疊加在HTTP協議之上的機制,REST的軟體依賴性更小。
- 不需要額外的資源發現機制。
- 在軟體技術演進中的長期的兼容性更好。
二、實例介紹
REST 定義了資源的通用訪問格式,接下來一個消費者為實例,介紹 RESTful API 定義:
- 獲取所有 users
GET /api/users
- 獲取指定 id 的 users
GET /api/users/100
- 新建一條 users 記錄
POST /api/users
- 更新一條 users 記錄
PUT /api/users/100
- 刪除一條 users 記錄
DELETE /api/users/100
- 獲取一個 users 的所有消費賬單
GET /api/users/100/bill
- 獲取一個 user 指定時間的消費賬單
GET /api/users/100/bill?from=201910&to=201911
以上其中 RESTful 風格 API 幾乎包含常見業務情況。
三、Nodejs 實現 RESTful API
1. 初始化 mock 數據
本案例使用 mock 數據來演示,如下:
{ "user1" : { "name" : "leo", "password" : "123456", "profession" : "teacher", "id": 1 }, "user2" : { "name" : "pingan8787", "password" : "654321", "profession" : "librarian", "id": 2 }, "user3" : { "name" : "robin", "password" : "888888", "profession" : "clerk", "id": 3 } }
我們將實現以下 RESTful API :
2. 獲取用戶列表
這一步我們會創建 RESTful API 中的/users,使用 GET 來讀取用戶的資訊列表:
// index.js const express = require('express'); const app = express(); const fs = require("fs"); // 定義 讀取用戶的資訊列表 的介面 app.get('/users', (req, res) => { fs.readFile( __dirname + "/" + "users.json", 'utf8', (err, data) => { console.log( data ); res.end( data ); }); }) const server = app.listen(8081, function () { const {address, port} = server.address(); console.log("server run in: http://%s:%s", address, port); })
3. 添加用戶
這一步我們會創建 RESTful API 中的/users,使用 POST 來添加用戶記錄:
// index.js // 省略之前文件 只展示需要實現的介面 // mock 一條要新增的數據 const user = { "user4" : { "name" : "pingan", "password" : "password4", "profession" : "teacher", "id": 4 } } // 定義 添加用戶記錄 的介面 app.post('/users', (req, res) => { // 讀取已存在的數據 fs.readFile( __dirname + "/" + "users.json", 'utf8', (err, data) => { data = JSON.parse( data ); data["user4"] = user["user4"]; console.log( data ); res.end( JSON.stringify(data)); }); })
4. 獲取用戶詳情
這一步我們在 RESTful API 中的 URI 後面加上/users/:id,使用 GET 來獲取指定用戶詳情:
// index.js // 省略之前文件 只展示需要實現的介面 // 定義 獲取指定用戶詳情 的介面 app.get('/users/:id', (req, res) => { // 首先我們讀取已存在的用戶 fs.readFile( __dirname + "/" + "users.json", 'utf8', (err, data) => { data = JSON.parse( data ); const user = data["user" + req.params.id] console.log( user ); res.end( JSON.stringify(user)); }); })
5. 刪除指定用戶
這一步我們會創建 RESTful API 中的/users,使用 DELETE 來刪除指定用戶:
// index.js // 省略之前文件 只展示需要實現的介面 // mock 一條要刪除的用戶id const id = 2; app.delete('/users', (req, res) => { fs.readFile( __dirname + "/" + "users.json", 'utf8', (err, data) => { data = JSON.parse( data ); delete data["user" + id]; console.log( data ); res.end( JSON.stringify(data)); }); })
四、REST 最佳實踐
1. URL 設計
1.1 "動詞 + 賓語"的操作指令結構
客戶端發出的數據操作指令都是"動詞 + 賓語"的結構。
如上面提到的,GET /user
這個命令,GET
是動詞,/user
是賓語。根據 HTTP 規範,動詞一律大寫。
動詞通常有以下五種 HTTP 方法:
GET:讀取(Read)
POST:新建(Create)
PUT:更新(Update)
PATCH:更新(Update),通常是部分更新
DELETE:刪除(Delete)
1.2 賓語必須是名詞
賓語就是 API 的 URL,是 HTTP 動詞作用的對象。它應該是名詞,不能是動詞。
比如,/users
是正確的,因為 URL 是名詞,而下面就都是錯誤的了:
/getUsers /createUsers /deleteUsers
1.3 建議複數 URL
因為 URL 是名詞,沒有單複數的限制,但是還是建議如果是一個集合,就使用複數形式。如GET /users
來讀取所有用戶列表。
1.4 避免多級 URL
避免在多層級資源時,使用多級 URL。常見案例如獲取某位用戶的購買過的某一類商品:
GET /users/100/product/120
這種 URL 語意不明,也不利拓展,建議只有第一級,其他級別用查詢字元串來表達:
GET /users/100?product=120
2. 準確的狀態碼錶示
HTTP 五大類狀態碼有100多種,每一種狀態碼都有標準的(或者約定的)解釋,客戶端只需查看狀態碼,就可以判斷出發生了什麼情況,所以伺服器應該返回儘可能精確的狀態碼。
這邊列舉幾個經常使用的狀態碼介紹:
- 303 See Other:表示參考另一個 URL。
- 400 Bad Request:伺服器不理解客戶端的請求,未做任何處理。
- 401 Unauthorized:用戶未提供身份驗證憑據,或者沒有通過身份驗證。
- 403 Forbidden:用戶通過了身份驗證,但是不具有訪問資源所需的許可權。
- 404 Not Found:所請求的資源不存在,或不可用。
- 405 Method Not Allowed:用戶已經通過身份驗證,但是所用的 HTTP 方法不在他的許可權之內。
- 410 Gone:所請求的資源已從這個地址轉移,不再可用。
- 415 Unsupported Media Type:客戶端要求的返回格式不支援。比如,API 只能返回 JSON 格式,但是客戶端要求返回 XML 格式。
- 422 Unprocessable Entity:客戶端上傳的附件無法處理,導致請求失敗。
- 429 Too Many Requests:客戶端的請求次數超過限額。
- 500 Internal Server Error:客戶端請求有效,伺服器處理時發生了意外。
- 503 Service Unavailable:伺服器無法處理請求,一般用於網站維護狀態。
3. 服務端響應
3.1 應該返回 JSON 對象
API 返回的數據格式應該是 JSON 一個對象。
3.2 發生錯誤時,不要返回 200 狀態碼
在發生錯誤時,如果還返回 200 狀態碼,前端需要解析返回數據才知道錯誤資訊,這樣實際上取消了狀態碼,是不恰當的。
正確的做法應該是在錯誤時,返回對應錯誤狀態碼,並將錯誤資訊返回:
HTTP/1.1 400 Bad Request Content-Type: application/json { "error": "Invalid payoad.", "detail": { "surname": "This field is required." } }
參考資料
- 《維基百科 – 表現層狀態轉換》
- 《RESTful風格的springMVC》
- 《Node.js RESTful API》
- 《RESTful API 最佳實踐》