前言
越來越多的Web應用程序使用JSON作為API的一種數據交換格式進行交互。本文檔的目標是使HTTP JSON API的設計風格保持一致,容易被理解和維護。一個優秀的API,應該是在其生命周期內能夠持續提供穩定、易用、受信任的服務,並且在API的生命周期結束時能讓其平滑的消亡。
注:RESTful API是目前比較成熟的一套Web應用程序的API設計理論,本文不對RESTful API過多介紹。在實際快速增長和多變的業務應用中,采用RESTful API需要更高的成本和對后端開發人員有更高的要求,我們更多采用這種輕量化的HTTP JSON API的設計。
約定
在本文檔中,使用的關鍵字會以中文+中括號包含的關鍵字英文表示:必須[MUST]。關鍵字”MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL”按照RFC 2119中的描述進行解釋。
JSON數據類型
JSON(JavaScript Object Notation)是一種輕量級,基於文本,語言無關的數據交換格式。其包括了4種基本數據類型和2種結構數據類型,共6種數據類型。
基本數據類型
- String 表示一個字符串。
- Number 可以表示整數和浮點數。
- Boolean 可以表示真假,值為true或false。
- Null 通常用於表示空對象。
“true”和true,這兩個數據代表的是不同的數據類型。非字符串類型數據輸出時一定不要[MUST NOT]為兩端加上雙引號,否則可能產生不希望的后果(如if中判斷”false”的結果是true)。其他容易產生錯誤的例子如:0和”0″等。
結構數據類型
- Object(對象)是無序的集合,以鍵值對的方式保持數據。一個Object中包含零到多個name/value的數據,數據間以逗號(,)分隔。name為String類型,value可以是任意類型的數據。Object的最后一個元素之后一定不要[MUST NOT]加上分隔符的逗號,否則可能導致解析出錯。
- Array(數組)為多個值的有序集合,數組元素間以逗號(,)分隔。
協議
使用HTTP或HTTPS協議。
URL規范
URL代表所提供的API的唯一性和永久性,在此之前我們應該[SHOULD]設計合理的URL:
必須[MUST]全部使用小寫字母拼寫URL
// good
http://www.example.com/api/v1/users?orderby=name
// bad
http://www.example.com/API/V1/users?orderBy=name
必須[MUST]使用破折號 –
// good
http://www.example.com/api/v1/user-info
// bad
http://www.example.com/api/v1/user_info
破壞性行為(create,delete,update)必須[MUST]使用POST方法
// good
POST http://www.example.com/api/v1/user/delete
// bad
GET http://www.example.com/api/v1/user/detete?id=123
建議[RECOMMENDED]使用容易理解的英文單詞
// good
POST http://www.example.com/api/v1/user/list
// bad
GET http://www.example.com/api/v1/user/operate
HTTP響應頭
status
http響應的status必須(MUST)為200。通常JSON數據被用於通過XMLHttpRequest對象訪問,通過javascript進行處理。返回錯誤的狀態碼可能導致錯誤不被響應,數據不被處理。
Content-Type
Content-Type字段定義了響應體的類型。一般情況下,瀏覽器會根據該類型對內容進行正確的處理。對於傳輸JSON數據的響應,Content-Type推薦(RECOMMENDED)設置為”text/javascript”或”text/plain”。 避免(MUST NOT)將Context-Type設置為text/html,否則可能導致安全問題。
Content-Type中可以指定字符集。通常 需要(SHOULD)明確指定一個字符集。如果是通過XMLHTTPRequest請求的數據,並且字符編碼為UTF-8時,可以不指定字符集。
Content-Type 示例
text/javascript;charset=UTF-8
HTTP響應體
返回的數據包含在HTTP響應體中。數據必須[MUST]是一個JSON Object。該Object可能[SHOULD]包含3個字段:code,msg,data。
{
code: 200,
msg: 'success',
data: {
xxx: '123'
}
}
code
code 字段被設計為業務自定義的狀態碼
, 必須(MUST)是一個不小於0的JSON Number整數,表示請求的狀態。這個字段 不可以(SHOULD NOT)被省略。
是否要在API里面自定義業務狀態碼,非常具有爭議,因為Http請求本身已經有了完備的狀態碼,再定義一套狀態碼直觀上感受多此一舉,但在實際開發中,可能由於用戶未登錄、登錄過期而有不同的返回結果和處理方式,所以必須[MUST]存在code字段。
狀態碼的定義也最好有一套規范,如按照用戶相關、授權相關、各種業務,做簡單的分類:
// 授權相關
1001: 無權限訪問
1002: access_token過期
1003: unique_token無效
...
// 用戶相關
2001: 未登錄
2002: 用戶信息錯誤
2003: 用戶不存在
// 業務1
3001: 業務1XXX
3002: 業務1XXX
// ...
msg
msg字段通常[SHOULD]是一個JSON String或JSON Object,表示除了請求狀態外服務端想要對本次請求做出的說明,使客戶端能夠獲取更多信息進行后續處理。這個字段是可選的[OPTIONAL] 。下面的兩個例子中,msg字段的信息都可以用於客戶端程序的后續處理,但是粒度和處理方式會有不同。
簡單說明的msg:
{
"code": 1,
"msg": "參數錯誤"
}
具有更多信息的msg:
{
"code": 1,
"msg": {
"text": "參數錯誤",
"parameters": {
"ticket": "ticket參數無效"
}
}
}
data
data字段可以是任意JSON類型,表示請求返回的數據主體。這個字段是可選的[OPTIONAL]。數據主體data包含了在請求成功時有意義的數據。
一個查詢姓名請求的返回數據:
{
"code": 200,
"data": "John"
}
一個查詢用戶信息請求的返回數據:
{
"code": 200,
"data": {
"username": "John",
"age": "31",
"gender": "male"
}
}
一個查詢用戶列表信息請求的返回數據:
{
"code": 200,
"data": [
{
"username": "John",
"age": "31",
"gender": "male"
},
{
"username": "Lily",
"age": "28",
"gender": "female"
}
]
}
數據場景
本章為常見數據場景定義了通用的標准數據格式,用於數據傳輸和使用。
變通數據格式必須[MUST]是一個JSON Object,其中必須[MUST]包含e-type屬性和data屬性。e-type屬性標識數據類型,便於對數據進行解析;data屬性包含變通后的數據。變通數據可以[MAY]包含其他的屬性,標識數據的其他擴展信息。
變通數據格式的e-type屬性定義了table值。e-type屬性可以使用者擴展其他屬性值,擴展的屬性值必須[MUST]以“項目縮寫-名稱”命名,如“fc-list”,自主解析。
日期類型
日期類型不屬於JSON數據類型。對於日期類型,我們必須[MUST]使用JSON String來表示。為了讓日期能夠更容易的被顯示和被解析,對於日期我們應當[SHOULD)]使用更適合Internet的格式,遵循RFC 3339。
日期展示格式
用來將日期展示給前端或者前端回傳給后端的格式:
// 一般日期格式
2018-12-6 11:21:08
// 時間戳格式(十位秒級)
1544066565
// 示例
{
code: 0,
msg: 'success',
data: '2018-12-6 11:21:08'
}
記錄項
記錄項代表二維表中的一行,通常用於表示某個具體事務抽象的屬性。標准記錄項數據必須[MUST]為一個JSON Object,記錄項的主鍵命名必須[MUST]為“id”。
標准記錄項
{
code: 0,
msg: 'success',
data: {
"id": 1,
"name": "John",
"sex": "male",
"age": 31
}
}
變通記錄項
{
code: 0,
msg: 'success',
data: [
{
label: '記錄ID',
value: 1,
type: 'number'
},
{
label: '用戶名稱',
value: 'John',
type: 'string'
},
{
label: '用戶性別',
value: 'male',
},
{
lable: '用戶年齡',
value: 31
}
]
}
二維表
二維表類型表識為table,是關系模型的主要數據結構。二維表結構具有變通數據格式。標准二維表數據必須[MUST]以一維JSON Array形式表示,JSON Array中每一項是一個JSON Object,代表一條記錄。JSON Object的每個成員代表一個字段。每條記錄的主鍵命名必須[MUST]為”id”。
在標准二維表中,字段名在每條記錄中都被傳輸,會造成額外的數據量傳輸。這個問題會隨着記錄數的增大會更加突出。為了減少傳輸數據量,變通格式使用二維JSON Array傳輸數據,擴展fields屬性用於字段說明。fields字段為JSON Array。
標准二維表
{
code: 0,
msg: 'success',
data: [
{
"id": 1,
"name": "John",
"sex": "male",
"age": 31
},
{
"id": 2,
"name": "Lily",
"sex": "female",
"age": 28
}
]
}
變通二維表
{
code: 0,
msg: 'success',
data: {
"e-type": "table",
"fields": ["id", "name", "sex", "age"],
"data": [
[1, "John", "male", 31],
[2, "Lily", "female", 28]
]
}
}
鍵值對
在一個JSON Object中表示鍵/值對:
- 鍵的屬性名必須[MUST]為name, 杜絕[MUST NOT]使用key或k
- 值的屬性名必須[MUST]為value, 杜絕[MUST NOT]使用v。
- 可以[MAY]為其擴展屬性名label,一般同name值相同。
簡單鍵/值
{
code: 0,
msg: 'success',
data: {
"name": "John",
"value": 1,
"lable": "John" // 可選
}
}
有序集合
鍵/值有序集合表示對事務或邏輯類型的抽象與分類。常見的應用場景有單選復選框集合,下拉菜單等。
標准的鍵/值有序集合是一個JSON Array,集合中的每一項是一個JSON Object。項 必須[MUST] 包含name和value屬性。 可以[MAY] 通過其他的屬性修飾每一項的特殊信息,如selected。
{
code: 0,
msg: 'success',
data: [
{
"name": "不滿意",
"value": 0,
"selected": true
},
{
"name": "滿意",
"value": 1
},
{
"name": "非常滿意",
"value": 2,
"selected": true
}
]
}
數據頁
數據頁是列表數據常用的數據方式,可能通過查詢或翻頁獲得數據。數據頁是二維表數據的包裝,包含列表數據本身更多的信息。
數據頁必須[MUST]是一個JSON Object,其中必須[MUST]包含的屬性為data。data是一個二維表。數據頁可以包括一些可選[OPTIONAL]的屬性,表示當前數據頁的信息。下表列舉了數據頁的可選屬性。
參數/屬性
- pageNumber{Number} – 當前頁碼,計數必須[MUST]為不小於1的整數,從1開始。通常簡寫為:pn
- pageSize{Number} – 每頁顯示條數, 必須[MUST]大於0。通常簡寫為:ps
- total{Number} – 列表總記錄數, 必須[MUST]為不小於0的整數。表示當前條件下所有記錄的數目,非本頁的記錄數。
- keyword{String} – 列表所屬的搜索關鍵字。
- orderBy{String} – 列表排序規則。多個排序規則之間以逗號分割(,);正序或倒序以asc或desc表示,與字段名之間以一個空格間隔。例:”id desc,name asc”
- condition{Object} – 列表所屬的搜索條件集合。屬性中可以包含或不包含keyword字段,如果不包含,建議(RECOMMMANDED)在解析的時候附加搜索關鍵字keyword條件。
- startTime{Datetime} – 開始時間,用來搜索帶有創建時間的列表數據,一般跟endTime成對出現
- endTime{Datetime} – 結束時間,同上
數據頁示例
{
code: 0,
msg: 'success',
data: {
"pn": 1,
"ps": 10,
"total": 100,
"keyword": "John",
"orderBy": "id desc, name asc",
"condition": {},
"startTime": "2010-11-11 11:11:11",
"endTime": "2018-11-11 11:11:11",
"data": [
{
"id": 1,
"name": "John",
"sex": "male",
"age": 31
},
{
"id": 2,
"name": "Lily",
"sex": "female",
"age": 28
}
]
}
}
樹結構
樹形數據用於表示層疊的數據結構。樹型數據必須[MUST]是一個JSON Object,代表樹型數據的根節點。下面是標准定義的可選節點列表,不在列表中的屬性可以[SHOULD]自行擴展。
節點屬性
- id {Number|String} – 節點的唯一標識。
- text {String}- 名稱或用於顯示的字符串。
- children {Array} – 子節點列表。
數據示例
// good
{
code: 0,
msg: 'success',
data: {
"id": 1,
"text": "中國",
"children": [
{
"id": 10,
"text": "北京",
"children": [
{
"id": 100,
"text": "東城區"
},
{
"id": 101,
"text": "西城區"
},
{
"id": 102,
"text": "海淀區"
}
]
},
{
"id": 31,
"text": "海南",
"children": [
{
"id": 600,
"text": "海口"
},
{
"id": 601,
"text": "三亞"
},
{
"id": 602,
"text": "五指山"
}
]
}
]
}
}
//bad
{
code: 0,
msg: 'success',
data: [
{
"id": 1,
"text": "中國",
"parentId": 0,
},
{
"id": 2,
"text": "北京",
"parentId": 1,
},
{
"id": 3,
"text": "東城區",
"parentId": 2,
},
{
"id": 4,
"text": "西城區",
"parentId": 2,
}
]
}