RESTful設(shè)計方法和規(guī)范
在初步了解了 RESTful 之后,我們接到一項(xiàng)任務(wù),需要為一所學(xué)校開發(fā)一套師生管理系統(tǒng),客戶要求所開發(fā)的系統(tǒng)能在 PC 桌面通過瀏覽器使用,而且日后還想開發(fā) IOS 和 Android 應(yīng)用。了解需求之后,我們毫不猶豫選擇了前后端分離的開發(fā)模式,并且決定遵從時下最為流行的 RESTful 規(guī)范。接下來,我們就以后端開發(fā)人員的角色,一起來了解整個開發(fā)過程。
1. 域名(Domain)
根據(jù) RESTful 規(guī)范,應(yīng)該盡量使用專用的域名用于部署 API,于是我們和校方溝通,使用下方域名作為 API 訪問地址:
https://api.demo.com
但是經(jīng)過溝通,發(fā)現(xiàn)上述域名已被占用,校方否決了我們的提議,考慮到 API 相對簡單,于是我們使用下面地址部署 API:
https://www.demo.com/api
上述地址中,https 代表協(xié)議名稱,常見的還有 http,二者區(qū)別在于前者在傳輸過程中是將信息加密后傳輸?shù)?,而后者是明文傳輸?strong>www.demo.com 為域名,可以理解成某個機(jī)房里一臺電腦的地址,通過這個地址,就能訪問這臺電腦提供的資源;api 代表一個資源路徑,可以想象成這臺電腦中一個文件夾的路徑。
2. 版本(Versioning)
師生管理系統(tǒng)不是一成不變的,日后還要更新維護(hù)。為了區(qū)分不同版本,API 的 URL 中應(yīng)當(dāng)包含 API 版本信息:
http://www.demo.com/api/1.0/foo
http://www.demo.com/api/1.1/foo
http://www.demo.com/api/2.0/foo
除了上述方法外,API 版本信息還可放在 HTTP 請求頭中。Github 采用的就是這種做法。
因?yàn)椴煌陌姹?,可以理解成同一種資源的不同表現(xiàn)形式,所以應(yīng)該采用同一個 URL。版本號可以在 HTTP 請求頭信息的 Accept 字段中進(jìn)行區(qū)分(參見Versioning REST Services):
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0
實(shí)際工作中,通常采用第一種方法,因?yàn)檫@樣的方式更加直觀,方便使用。
3. 路徑(Endpoint)
路徑即"終點(diǎn)"(endpoint),是訪問 API 的具體網(wǎng)址,通過訪問每個網(wǎng)址,可以獲取到相應(yīng)的資源(resource)。在師生管理系統(tǒng)中,所謂資源,就是我們想獲取的信息,比如獲取 3 年 2 班所有學(xué)生姓名,獲取小明的年齡、成績等。
路徑須滿足以下規(guī)范:
1. 資源路徑中應(yīng)當(dāng)使用名詞,杜絕動詞。資源路徑中的名詞,應(yīng)當(dāng)與數(shù)據(jù)庫的表名相對應(yīng)。
以下路徑中包含動詞,是不符合規(guī)范的例子,在實(shí)際工作中,應(yīng)當(dāng)避免。
/getStudents :獲取學(xué)生信息
/listTeachers :獲取老師信息
/retreiveStudentByID?Id=2020 :獲取ID為2020的學(xué)生信息
對于資源的操作,應(yīng)該通過 HTTP 中的不同方法來區(qū)分處理資源的動作,資源路徑中應(yīng)當(dāng)只包含名詞。
GET /students :將返回所有學(xué)生信息
POST /students :將新增的學(xué)生信息存入數(shù)據(jù)庫
GET /students/4 :獲取編號為4號的學(xué)生信息
PATCH(或)PUT /students/4 :更新編號為4的學(xué)生信息
2. API 中的名詞應(yīng)該使用復(fù)數(shù)。無論是子資源或者是所有資源。
例如:
獲取單個學(xué)生信息:http://www.demo.com/students/1 :獲取編號為1的學(xué)生信息
獲取所有學(xué)生信息: http://www.demo.com/students :獲取所有學(xué)生信息
4. HTTP動詞
對于資源的具體操作類型,由 HTTP 動詞表示。
常用的 HTTP 動詞有下面 4 個(括號里是對應(yīng)的 SQL 命令)。
- GET(SELECT):從服務(wù)器取出資源(一項(xiàng)或多項(xiàng))
- POST(CREATE):在服務(wù)器新建一個資源
- PUT(UPDATE):在服務(wù)器更新資源(客戶端提供改變后的完整資源)
- DELETE(DELETE):從服務(wù)器刪除資源
還有 3 個不常用的 HTTP 動詞。
- PATCH(UPDATE):在服務(wù)器更新(更新)資源(客戶端提供改變的屬性)
- HEAD:獲取資源的元數(shù)
- OPTIONS:獲取信息,關(guān)于資源的哪些屬性是客戶端可以改變的
下面是一些例子。
GET /classes:列出所有班級
POST /classes:新建一個班級(上傳文件)
GET /classes/ID:獲取某個指定班級的信息
PUT /classes/ID:更新某個指定班級的信息(提供該班級的全部信息)
PATCH /classes/ID:更新某個指定班級的信息(提供該班級的部分信息)
DELETE /classes/ID:刪除某個班級
GET /classes/ID/students:列出某個指定班級的所有學(xué)生
DELETE /classes/ID/students/ID:刪除某個指定班級的指定學(xué)生
5. 過濾信息(Filtering)
如果記錄數(shù)量很多,服務(wù)器不可能都將它們返回給用戶。API 應(yīng)該提供參數(shù),過濾返回結(jié)果。比如,我們想獲取全校師生的個人信息,如果將這些信息一股腦地全部展示在網(wǎng)頁上,是不明智也是不現(xiàn)實(shí)的。如果數(shù)據(jù)量太大,在實(shí)際開發(fā)中我們會采用分頁展示的形式。另外,如果想在一次考試后,按照成績高低展示學(xué)生信息,那么可以通過過濾信息來實(shí)現(xiàn)。
所謂過濾,就是在 URL 中添加一下限制參數(shù)。下面是一些常見的參數(shù)。
?limit=10:指定返回記錄的數(shù)量
?offset=10:指定返回記錄的開始位置。
?page=2&per_page=100:指定第幾頁,以及每頁的記錄數(shù)。
?sortby=score&order=asc:指定返回結(jié)果按照學(xué)生的成績(score)正序(asc)排列順序。
參數(shù)的設(shè)計允許存在冗余,即允許 API 路徑和 URL 參數(shù)允許有重復(fù)。比如,想要查詢某個班級所有學(xué)生信息,我們可以設(shè)計 GET /classes/ID/students
與 GET /students?class_id=ID
兩種地址,任何一種都可得到相同的結(jié)果。
6. 狀態(tài)碼(Status Codes)
服務(wù)器向用戶返回的狀態(tài)碼和提示信息,常見的有以下一些(方括號中是該狀態(tài)碼對應(yīng)的 HTTP 動詞)。不同的狀態(tài)碼代表著不同的含義,比如以 2 開頭的狀態(tài)碼通常代表服務(wù)器成功響應(yīng),3 開頭的狀態(tài)碼代表發(fā)生了重定性(即跳轉(zhuǎn)到了別的鏈接),4 開頭的狀態(tài)碼通常表示客戶端這邊提供的信息有誤,而 5 開頭的狀態(tài)碼則表示服務(wù)器內(nèi)部出現(xiàn)的錯誤。通過返回的狀態(tài)碼,用戶即可判斷請求成功與否,不成功問題在何處。
一些常用的狀態(tài)碼列舉如下:
- 200 OK - [GET]:服務(wù)器成功返回用戶請求的數(shù)據(jù)
- 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數(shù)據(jù)成功。
- 202 Accepted - [*]:表示一個請求已經(jīng)進(jìn)入后臺排隊(異步任務(wù))
- 204 NO CONTENT - [DELETE]:用戶刪除數(shù)據(jù)成功
- 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發(fā)出的請求有錯誤,服務(wù)器沒有進(jìn)行新建或修改數(shù)據(jù)的操作
- 401 Unauthorized - [*]:表示用戶沒有權(quán)限(令牌、用戶名、密碼錯誤)
- 403 Forbidden - [*] 表示用戶得到授權(quán)(與401錯誤相對),但是訪問是被禁止的
- 404 NOT FOUND - [*]:用戶發(fā)出的請求針對的是不存在的記錄,服務(wù)器沒有進(jìn)行操作,該操作是冪等的
- 406 Not Acceptable - [GET]:用戶請求的格式不可得(比如用戶請求JSON格式,但是只有XML格式)
- 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再得到的
- 422 Unprocesable entity - [POST/PUT/PATCH]: 當(dāng)創(chuàng)建一個對象時,發(fā)生一個驗(yàn)證錯誤
- 500 INTERNAL SERVER ERROR - [*]:服務(wù)器發(fā)生錯誤,用戶將無法判斷發(fā)出的請求是否成功
7. 錯誤信息
如果狀態(tài)碼是 4xx,服務(wù)器就應(yīng)該向用戶返回出錯信息。一般來說,返回的信息是鍵值對形式的數(shù)據(jù),將 error
作為鍵名,出錯信息作為鍵值即可。比如,在一個提供查詢學(xué)生信息的 API 中,要求客戶端提供正確的 API key(可以理解為輸入了正確的用戶名和密碼)才能訪問,如果提供的 API key 不正確,此時服務(wù)器應(yīng)拒絕訪問,并返回錯誤信息。這樣,客戶端就知道了為何沒能查到信息,修改成正確的 API key 即可。
{
error: "Invalid API key"
}
8. 返回結(jié)果
針對不同操作,服務(wù)器向用戶返回的結(jié)果應(yīng)該符合以下規(guī)范。
- GET /collection:返回資源對象的列表(數(shù)組)
- GET /collection/resource:返回單個資源對象
- POST /collection:返回新生成的資源對象
- PUT /collection/resource:返回完整的資源對象
- PATCH /collection/resource:返回完整的資源對象
- DELETE /collection/resource:返回一個空文檔
9. 超媒體鏈接
RESTful API 最好做到 Hypermedia(即返回結(jié)果中提供鏈接,連向其他 API 方法),使得用戶不查文檔,也知道下一步應(yīng)該做什么。
比如,Github 的 API 就是這種設(shè)計,訪問api.github.com會得到一個所有可用API的網(wǎng)址列表。
{
"current_user_url": "https://api.github.com/user",
"authorizations_url": "https://api.github.com/authorizations",
// ...
}
從上面可以看到,如果想獲取當(dāng)前用戶的信息,應(yīng)該去訪問 api.github.com/user,然后就得到了下面結(jié)果。
{
"message": "Requires authentication",
"documentation_url": "https://developer.github.com/v3"
}
上面代碼表示,服務(wù)器給出了提示信息,以及文檔的網(wǎng)址。
10. 數(shù)據(jù)格式
服務(wù)器返回的數(shù)據(jù)格式,應(yīng)該盡量使用 JSON,避免使用 XML。什么是 JSON 呢?什么又是 XML 呢?兩種數(shù)據(jù)格式的簡單舉例如下:
# JSON
{"name":"XiaoMing",
"age":"12",
"gender":"male"}
# XML
<?xml version="1.0" encoding="UTF-8" ?>
<name>XiaoMing</name>
<age>12</age>
<gender>male</gender>
通過上面的對比可以看出,JSON 數(shù)據(jù)形式要遠(yuǎn)比 XML 的數(shù)據(jù)形式來得簡單和易懂,所以現(xiàn)在的 Web 開發(fā)中 JSON 數(shù)據(jù)格式已經(jīng)開始全面取代 XML 應(yīng)用在實(shí)際開發(fā)中。
11. 小結(jié)
本節(jié)主要從域名、版本、路徑、HTTP動詞、過濾信息、狀態(tài)碼、錯誤信息、返回結(jié)果、超媒體鏈接、數(shù)據(jù)格式 10 個方面介紹了 RESTful 設(shè)計方法和設(shè)計規(guī)范。為了讓更多的人方便使用所設(shè)計的 API 接口,以上規(guī)范一定要遵守哦!