RESTful API設計原則與規範html
1、背景與基礎概念 2編程
2、RESTful API應遵循的原則 3json
1、協議(Protocol) 3api
2、域名(ROOT URL) 3數組
3、版本(Versioning) 3緩存
4、路徑(Endpoints) 3服務器
5、HTTP動詞(HTTP Verbs) 4網絡
6、過濾信息(Filtering) 5架構
7、狀態碼(Status Codes) 5app
8、錯誤處理(Error handling) 6
9、返回結果(Response) 6
10、使用HATEOAS的Hypermedia API 6
11、認證(Authentication) 7
3、Swagger API標準 7
REST,即Representational State Transfer的縮寫。RESTful架構,是目前最流行的一種互聯網軟件架構。它結構清晰、符合標準、易於理解、擴展方便,基於這個風格設計的軟件能夠更簡潔,更有層次,更易於實現緩存等機制,因此正獲得愈來愈多網站的採用。若是一個架構符合REST原則,就稱它爲RESTful架構。
本文即將描述的,便是建立RESTful架構的API所要遵循的原則與規範。
1、背景與基礎概念
Web 應用程序最重要的 REST 原則是,客戶端和服務器之間的交互在請求之間是無狀態的。從客戶端到服務器的每一個請求都必須包含理解請求所必需的信息。
- GET: 用來獲取資源
- POST: 用來新建資源
- PUT: 用來更新資源
- DELETE: 用來刪除資源
2、RESTful API應遵循的原則
1、協議(Protocol)
API與用戶的通訊協議,儘可能使用HTTPs協議。HTTPs協議的全部信息都是加密傳播,第三方沒法竊聽,具備校驗機制,一旦被篡改,通訊雙方會馬上發現,配備身份證書,防止身份被冒充。
2、域名(ROOT URL)
應該儘可能將API部署在專用域名之下。
https://api.example.com
若是肯定API很簡單,不會有進一步擴展,能夠考慮放在主域名下。
https://example.org/api/
3、版本(Versioning)
應該將API的版本號放入URL。
https://api.example.com/v1/
另外一種作法是,將版本號放在HTTP頭信息中,但不如放入URL方便和直觀。Github採用這種作法。
注:須要注意版本規劃,以便之後API的升級和維護。使得API版本變得強制性,不要發佈無版本的API,使用簡單數字,避免小數點如2.5。
4、路徑(Endpoints)
路徑表示API的具體網址URL。在RESTful架構中,每一個URL表明一種資源(resource),因此網址中不能有動詞,只能有名詞,並且所用的名詞每每與表明的對象名稱對應。通常來講,某一同種記錄的」集合」(collection),因此API中的名詞也應該使用複數。
具體細則:
1、使用名詞而不是動詞。舉例來講,某個URL是/cards/show/1,其中show是動詞,這個URL就設計錯了,正確的寫法應該是/cards/1,而後用GET方法表示show。若是某些動做是HTTP動詞表示不了的,你就應該把動做作成一種資源。好比網上匯款,從帳戶1向帳戶2匯款500元,錯誤的URI是:POST /accounts/1/transfer/500/to/2。正確的寫法是把動詞transfer改爲名詞transaction,資源不能是動詞,可是能夠是一種服務:POST /transaction?from=1&to=2&amount=500.00。
2、使用複數名詞。不要混淆名詞單數和複數,爲了保持簡單,只對全部資源使用複數。
舉例:
/cars 而不是 /car
/users 而不是 /user
/products 而不是 /product
/settings 而不是 /setting
3、使用子資源表達關係。若是一個資源與另一個資源有關係,使用子資源。
舉例:
GET /cars/911/drivers/ 返回 car 911的全部司機
GET /cars/911/drivers/8 返回 car 911的8號司機
5、HTTP動詞(HTTP Verbs)
對於資源的具體操做類型,由HTTP動詞表示。經常使用的HTTP動詞有下面五個:
還有兩個不經常使用的HTTP動詞。
注:Get方法和查詢參數不該該涉及狀態改變。使用PUT, POST 和DELETE方法而不是 GET 方法來改變狀態。
6、過濾信息(Filtering)
若是記錄數量不少,服務器不可能都將它們返回給用戶。API應該提供參數,過濾返回結果。爲集合提供過濾、排序、選擇和分頁等功能。
下面是一些常見的參數。
參數的設計容許存在冗餘,即容許API路徑和URL參數偶爾有重複。好比,GET /zoo/ID/animals 與 GET /animals?zoo_id=ID 的含義是相同的
注:
①移動端可以顯示其中一些字段,它們其實不須要一個資源的全部字段,給API消費者一個選擇字段的能力,這會下降網絡流量,提升API可用性。
②爲了將總數發給客戶端,使用訂製的HTTP頭: X-Total-Count.
7、狀態碼(Status Codes)
服務器向用戶返回的狀態碼和提示信息,常見的有如下一些(方括號中是該狀態碼對應的HTTP動詞)。
8、錯誤處理(Error handling)
若是狀態碼是4xx,就應該向用戶返回出錯信息。儘可能使用詳細的錯誤包裝信息:
{
"errors": [
{
"userMessage": "Sorry, the requested resource does not exist",
"internalMessage": "No car found in the database",
"code": 4xx,
"more info": "http://dev.example.com/api/v1/errors/12345"
}
]
}
9、返回結果(Response)
服務器返回的數據格式,應該儘可能使用JSON,避免使用XML。針對不一樣操做,服務器向用戶返回的結果應該符合如下規範。
10、使用HATEOAS的Hypermedia API
RESTful API最好使用Hypermedia as the Engine of Application State(超媒體做爲應用狀態的引擎),即返回結果中提供連接,連向其餘API方法,超文本連接能夠創建更好的文本瀏覽,使得用戶不查文檔,也知道下一步應該作什麼。
好比,當用戶向api.example.com的根目錄發出請求,會獲得這樣一個文檔。
{"link": {
"rel": "collection https://www.example.com/zoos",
"href": "https://api.example.com/zoos",
"title": "List of zoos",
"type": "application/vnd.yourformat+json"
}}
上面代碼表示,文檔中有一個link屬性,用戶讀取這個屬性就知道下一步該調用什麼API了。rel表示這個API與當前網址的關係(collection關係,並給出該collection的網址),href表示API的路徑,title表示API的標題,type表示返回類型。
11、認證(Authentication)
API的身份認證儘可能使用OAuth 2.0框架。
3、Swagger API標準
API的文檔管理和信息描述,將使用Swagger進行。
Swagger是一個規範和完整的框架,用於生成、描述、調用和可視化RESTful風格的Web服務。Swagger的目標是對REST API定義一個標準的和語言無關的接口,可以讓人和計算機無需訪問源碼、文檔或網絡流量監測就能夠發現和理解服務的能力。
Swagger規範定義了一組描述一個API所需的文件格式,相似於描述Web服務的WSDL。經過Swagger進行REST API的正肯定義,用戶能夠理解遠程服務並使用最少實現邏輯與遠程服務進行交互。與爲底層編程所實現的接口相似,Swagger消除了調用服務時可能會有的猜想。
注:Microsoft,PayPal等公司也已經引入Swagger 來定義本身的REST API 文檔。
Swagger API可使用YAML或JSON來表示。
Swagger這類API文檔工具能夠知足下列需求:
一般狀況下,API的Swagger描述爲JSON文件,也可以使用YAML描述的文件。
附Swagger文件示例:
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Swagger Petstore (Simple)",
"description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
"termsOfService": "http://helloreverb.com/terms/",
"contact": {
"name": "Swagger API team",
"email": "foo@example.com",
"url": "http://swagger.io"
},
"license": {
"name": "MIT",
"url": "http://opensource.org/licenses/MIT"
}
},
"host": "petstore.swagger.io",
"basePath": "/api",
"schemes": [
"http"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/pets": {
"get": {
"description": "Returns all pets from the system that the user has access to",
"operationId": "findPets",
"produces": [
"application/json",
"application/xml",
"text/xml",
"text/html"
],
"parameters": [
{
"name": "tags",
"in": "query",
"description": "tags to filter by",
"required": false,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "csv"
},
{
"name": "limit",
"in": "query",
"description": "maximum number of results to return",
"required": false,
"type": "integer",
"format": "int32"
}
],
"responses": {
"200": {
"description": "pet response",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/pet"
}
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/errorModel"
}
}
}
},
"post": {
"description": "Creates a new pet in the store. Duplicates are allowed",
"operationId": "addPet",
"produces": [
"application/json"
],
"parameters": [
{
"name": "pet",
"in": "body",
"description": "Pet to add to the store",
"required": true,
"schema": {
"$ref": "#/definitions/newPet"
}
}
],
"responses": {
"200": {
"description": "pet response",
"schema": {
"$ref": "#/definitions/pet"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/errorModel"
}
}
}
}
},
"/pets/{id}": {
"get": {
"description": "Returns a user based on a single ID, if the user does not have access to the pet",
"operationId": "findPetById",
"produces": [
"application/json",
"application/xml",
"text/xml",
"text/html"
],
"parameters": [
{
"name": "id",
"in": "path",
"description": "ID of pet to fetch",
"required": true,
"type": "integer",
"format": "int64"
}
],
"responses": {
"200": {
"description": "pet response",
"schema": {
"$ref": "#/definitions/pet"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/errorModel"
}
}
}
},
"delete": {
"description": "deletes a single pet based on the ID supplied",
"operationId": "deletePet",
"parameters": [
{
"name": "id",
"in": "path",
"description": "ID of pet to delete",
"required": true,
"type": "integer",
"format": "int64"
}
],
"responses": {
"204": {
"description": "pet deleted"
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/errorModel"
}
}
}
}
}
},
"definitions": {
"pet": {
"type": "object",
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
},
"newPet": {
"type": "object",
"required": [
"name"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
},
"errorModel": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
}
}
}
}
}
Swagger文件的構成以及規範信息,在Swagger官方的規範指導《Swagger RESTful API文檔規範》中有詳細描述,請參看。