RESTful API設計原則與規範

RESTful API設計原則與規範html

 

 

1、背景與基礎概念 2編程

2、RESTful API應遵循的原則 3json

1、協議(Protocol) 3api

2、域名(ROOT URL) 3數組

3、版本(Versioning) 3緩存

4、路徑(Endpoints) 3服務器

5HTTP動詞(HTTP Verbs) 4網絡

6、過濾信息(Filtering 5架構

7、狀態碼(Status Codes 5app

8、錯誤處理(Error handling 6

9、返回結果(Response 6

10、使用HATEOASHypermedia API 6

11、認證(Authentication 7

3、Swagger API標準 7

 

REST,即Representational State Transfer的縮寫。RESTful架構,是目前最流行的一種互聯網軟件架構。它結構清晰、符合標準、易於理解、擴展方便,基於這個風格設計的軟件能夠更簡潔,更有層次,更易於實現緩存等機制,因此正獲得愈來愈多網站的採用。若是一個架構符合REST原則,就稱它爲RESTful架構。

本文即將描述的,便是建立RESTful架構的API所要遵循的原則與規範。

1、背景與基礎概念

Web 應用程序最重要的 REST 原則是,客戶端和服務器之間的交互在請求之間是無狀態的。從客戶端到服務器的每一個請求都必須包含理解請求所必需的信息。

  • 資源(resource):網絡上的一個實體或者說是一個具體信息,能夠是一段文本、一張圖片、一首歌曲、一種服務。
  • 統一資源定位符(URIUniversal Resource Identifier):一個資源的識別符或者說是一個地址,經過URI你能夠定位到特定的資源。要獲取這個資源,須要訪問它的URI,所以,URI就成了每個資源的地址或獨一無二的識別符。
  • 狀態轉換(State Transfer: 全部資源都共享統一的接口,以便在客戶端和服務器之間傳輸狀態。客戶端與服務器互動的過程,一般涉及到服務器端數據和狀態的變化過程,好比文件被修改,訪問數量增長等。使用的是標準的 HTTP 方法,Http標準中定義的最主要四個動詞:GETPOSTPUTDELETE。它們分別對應四種基本操做:

- GET 用來獲取資源

- POST 用來新建資源

- PUT 用來更新資源

- DELETE 用來刪除資源

  • Hypermedia 是應用程序狀態的引擎,資源表示經過超連接互聯。

 

 

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 9118號司機

 

5HTTP動詞(HTTP Verbs)

對於資源的具體操做類型,由HTTP動詞表示。經常使用的HTTP動詞有下面五個:

  • GETSELECT):從服務器取出資源(一項或多項)。
  • POSTCREATE):在服務器新建一個資源。
  • PUTUPDATE):在服務器更新資源(客戶端提供改變後的完整資源)。
  • PATCHUPDATE):在服務器更新資源(客戶端提供改變的屬性)。
  • DELETEDELETE):從服務器刪除資源。

還有兩個不經常使用的HTTP動詞。

  • HEAD:獲取資源的元數據。
  • OPTIONS:獲取信息,關於資源的哪些屬性是客戶端能夠改變的。

注:Get方法和查詢參數不該該涉及狀態改變。使用PUT, POST DELETE方法而不是 GET 方法來改變狀態。

 

6、過濾信息(Filtering

若是記錄數量不少,服務器不可能都將它們返回給用戶。API應該提供參數,過濾返回結果。爲集合提供過濾、排序、選擇和分頁等功能。

下面是一些常見的參數。

  • ?limit=10:指定返回記錄的數量
  • ?offset=10:指定返回記錄的開始位置。
  • ?pageNumber=2&perSize=100:指定第幾頁,以及每頁的記錄數。
  • ?sortby=name&order=asc:指定返回結果按照哪一個屬性排序,以及排序順序。
  • ?animal_type_id=1:指定篩選條件

參數的設計容許存在冗餘,即容許API路徑和URL參數偶爾有重複。好比,GET /zoo/ID/animals GET /animals?zoo_id=ID 的含義是相同的

注:

移動端可以顯示其中一些字段,它們其實不須要一個資源的全部字段,給API消費者一個選擇字段的能力,這會下降網絡流量,提升API可用性。

爲了將總數發給客戶端,使用訂製的HTTP頭: X-Total-Count.

 

7、狀態碼(Status Codes

服務器向用戶返回的狀態碼和提示信息,常見的有如下一些(方括號中是該狀態碼對應的HTTP動詞)。

  • 200 OK - [GET]:服務器成功返回用戶請求的數據,該操做是冪等的(Idempotent)。
  • 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。
  • 202 Accepted - [*]:表示一個請求已經進入後臺排隊(異步任務)
  • 204 NO CONTENT - [DELETE]:用戶刪除數據成功。
  • 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操做,該操做是冪等的。
  • 401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。
  • 403 Forbidden - [*]:表示用戶獲得受權(與401錯誤相對),可是訪問是被禁止的。
  • 404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操做,該操做是冪等的。
  • 406 Not Acceptable - [GET]:用戶請求的格式不可得(好比用戶請求JSON格式,可是隻有XML格式)。
  • 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再獲得的。
  • 422 Unprocesable entity - [POST/PUT/PATCH]:當建立一個對象時,發生一個驗證錯誤。
  • 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將沒法判斷髮出的請求是否成功。

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。針對不一樣操做,服務器向用戶返回的結果應該符合如下規範。

  • GET /collection:返回資源對象的列表(數組)
  • GET /collection/resource:返回單個資源對象
  • POST /collection:返回新生成的資源對象
  • PUT /collection/resource:返回完整的資源對象
  • PATCH /collection/resource:返回完整的資源對象
  • DELETE /collection/resource:返回一個空文檔

10、使用HATEOASHypermedia 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可使用YAMLJSON來表示。

Swagger這類API文檔工具能夠知足下列需求:

  • 支持API自動生成同步的在線文檔
  • 這些文檔可用於項目內部API審覈
  • 方便測試人員瞭解API
  • 這些文檔可做爲客戶產品文檔的一部分進行發佈
  • 支持API規範生成代碼,生成的客戶端和服務器端骨架代碼能夠加速開發和測試速度

一般狀況下,APISwagger描述爲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文檔規範》中有詳細描述,請參看。

http://swagger.io/specification/

相關文章
相關標籤/搜索