前後端API規範參考
規範能夠大量減少溝通成本,另外筆者認為它還有一個隱性的作用,如果有了規範,在寫程式碼的時候可以省略很多思考和修改,從而增加開發效率。所以筆者一直對規範這塊有著一種執念,總會在規範方面想很多。比如最近就在思考API的一些規範,參考RESTful API的思想,微創新出了一套過渡性的類REST規範,可以在僅用GET、POST的情況下,形成一定的規範。
下面用一個 公司列表 -> 公司詳情 -> 部門列表 的場景來舉例對比
RESTful | 類RESTful | 隨心所欲版 | |
---|---|---|---|
公司-查 | GET /corps | GET /corps | GET /corps |
公司-增 | POST /corps | POST /corps | POST /corp/create |
公司-刪 | DELETE /corps/:corpID | POST /corps/:corpID/delete | POST /corp/delete |
公司-改 | PUT /corps/:corpID | POST /corps/:corpID/put | POST /corp/modify |
公司-區域性改 | PATCH /corps/:corpID | POST /corps/:corpID/patch | POST /corp/update |
公司詳情-查 | GET /corps/:corpID | GET /corps/:corpID | GET /corp |
部門-查 | GET /corps/:corpID/departs | GET /corps/:corpID/departs | GET /depart/list |
部門-增 | POST /corps/:corpID/departs | POST /corps/:corpID/departs | POST /depart/add |
部門-刪 | DELETE /corps/:corpID/departs/:departID | POST /corps/:corpID/departs/:departID/delete | POST /depart/remove |
部門-改 | PUT /corps/:corpID/departs/:departID | POST /corps/:corpID/departs/:departID/put | POST /depart/save |
部門-區域性改 | PATCH /corps/:corpID/departs/:departID | POST /corps/:corpID/departs/:departID/patch | POST /depart/change |
類RESTful規範說明:
- 只可以在最後出現動詞
- 名詞要用複數
- “新建”時不需要動詞字尾,POST+名詞複數即可
- 區域性修改,如修改部門的leader、英文名等,統一用patch,修改的欄位在傳的data中宣告
如果自己的專案還有什麼特殊的需求,可適當修改為自己的規範。總之,要有一個清晰的!清晰的!清晰的!規範,來降低溝通成本,提高效率。
另外,由於類RESTful允許在最後增加動詞,這就較RESTful更為靈活,對於一些特殊的場景也可以應付自如。比如匯入、匯出、從其他資料來源同步等CURD之外的場景,就可以用不同的動詞,如import、export、sync等來進行區分。
再說說資料結構。如果有一個規範的資料結構,那麼無論對於前端還是後端來說,都有利於集中管理,封裝之後可以節省很多程式碼。
{
"code": 200,
"msg": "success",
"data": {
"corps": [
{
"id": 123,
"name": "A Corp"
}
],
"corp_count": 99,
"departs": [
{
"id": 456,
"name": "A depart"
}
],
"depart_count": 49
}
}
- code、msg、data必須有,且不多不少。
- code可簡單的定義為0、1,表示成功與否,也可參考HTTP響應碼
- msg為給使用者展示的資訊,可與code關聯,統一維護一個字典
- data必須為object,返回資料都要被其包裹。如果沒有資料返回時,至少要讓data返回一個空物件,即“{}”
最後說說前端請求的封裝。封裝後要達到以下效果:
-
返回結果統一處理,不用到處寫if(code===200){}else if(code===300){}之類的程式碼
-
封裝與業務隔離,更換其他庫的成本低,axios、jquery等說換就換,不用修改太多程式碼
-
支援特殊需求配置,比如成功後是否彈窗、彈窗文字變化等
-
支援快取,防止一些公用介面多次請求浪費資源
封裝層示意:
// ajax.js 封裝層示意
function ajaxBase(config) {
const { url, data, type = 'get', dataType = 'json', cache = false, succuss, error } = config;
$.ajax({
url,
data,
type,
dataType,
cache
}).done((res) => {
if (res.code === 200) { // 統一成功處理
if (succuss) {
succuss(res.data);
} else {
console.log('Success!');
}
} else if (res.code === 302) { // 統一跳轉
console.log('Redirect to other place!');
location.href = data.url;
} else { // 統一錯誤處理
if (error) {
error(res);
} else {
console.warn(JSON.stringify(res));
}
}
});
}
export function getAjax(url, data, sucFun, errFun) {
return ajaxBase({ url, data, succuss: sucFun, error: errFun });
}
export function postAjax(url, data, sucFun, errFun) {
return ajaxBase({ url, data, type: 'post', succuss: sucFun, error: errFun });
}
封裝層是統一管理請求的地方,此處儘量封裝公共邏輯,比如上例中的預設成功回撥、失敗回撥、以及不同返回碼的回撥等。
另外一個重點是暴露。不管上面的封裝用的是jquery、axios或是其他,此處暴露出去的api傳參一定要保持不變。這樣在更換ajax核心庫的時候,後面的業務層不需要做改動。目前,效果還沒有體現出來,下面來看下業務呼叫層的情況,將會省下很多程式碼。
業務層示意:
// 業務層封裝
import { getAjax, postAjax } from './ajax.js';
export function getUsers(data, sucFun) {
return getAjax('/users', data, sucFun);
}
export function updateUser(data, sucFun) {
return postAjax(`/users/${data.userId}`, data, sucFun);
}
// 業務層呼叫
getUsers({ page: 1, pageSize: 10 }, ({ data }) => {
// do success
});
// 走預設sucFun
updateUser({ userId: 123, name: 'userName' });
通常在業務層的模組裡,筆者也會做一次ajax的封裝,便於統一管理。在上例可以看到,業務層的封裝基本上就是把url傳了進去。另外,是否走cache也應該在這裡進行控制,為了不讓例子太複雜,筆者在這裡沒有體現出來,留給讀者自己實現。在最終的呼叫層可以看到,請求變得清晰簡單許多。
對於promise風格的封裝,可能就有些不同了,不過思路都是一樣的,只要能達到上面總結的各種效果,就是好的封裝。