在構建API時,咱們不可避免的會採用現有的跨平臺的HTTP的交互方式與資源模型,所以若是你發現你目前的模式與咱們的標準南轅北轍,那麼請諮詢大家專業的API設計師以得到進一步的建議。git
URI應當包含vN
,其中N
指明版本號。基於URL的版本控制相較於其餘複雜的請求頭的方法會顯得簡單易用不少。github
URI Templateweb
/v{version}/
Exampleapi
/v1/
若是在URI中你須要考慮命名空間這個概念,那麼應當選擇緊鄰在version
以後的第一個字段。命名空間折射出消費者對於API功能的觀點,而不必定是公司自己業務邏輯層級的劃分。緩存
URI Template架構
/{version}/{namespace}/
Exampleapp
/v1/vault/
URI與資源之間的關聯應當保證一致性,避免出現容易引發混淆的子命名空間或者子目錄的命名,這樣有助於使用者可以很明晰地構造這些請求的URI。webapp
URI Templateide
/{version}/{namespace}/{resource}/{resource-id}/{sub-resource}/{sub-resource-id}
支持CRUD操做的資源被稱爲是Collection Resources,每每這些資源會與POST/GET/PUT/PATCH/DELETE這些HTTP動詞緊密關聯。Collection Resources命名時應當使用複數名詞,譬如/users
,這樣能夠和下面說起的Singletons進行區分。
Verb | Usage | Idempotent:冪等性 | Notes |
---|---|---|---|
GET | Read | X | |
POST | Create | 僅當使用 PayPal-Request-Id 請求頭時具備冪等性 | |
PUT | Create | 僅當在客戶端提供了資源標識符時具備建立功能 | |
PUT | Update | X | 僅用於某個資源的所有屬性的更新,不可用於局部 |
PATCH | Update | 使用JSON Patch 消息格式 | |
DELETE | Delete | 應當在屢次請求下具備相同的響應 |
包含任何相關聯的元信息的給定資源的列表,全部的資源應當包含在items
域中,而相似於total_items
以及 total_pages
的域指明整個關於數據總體的信息。這種命名的一致性有助於客戶端開發者構建面向不一樣的資源集合的通用的處理函數。若是使用GET
動做進行訪問,那麼注意不該該影響到整個系統,而且保證除非數據發送變化不然響應消息也應該保持一致性。另外還須要注意的是,譬如日誌輸出這種動做不會被認爲是對系統的修改。
在API客戶端權限合規的狀況下容許對於資源列表進行過濾操做,即並非本次都要把所有資源進行返回。另外,咱們須要提供一個簡短的摘要性質的資源表述來減小帶寬的消耗,通常來講單個資源都包含較多的屬性。
關於分頁的操做應該來源於請求時的page
與page_size
參數,其中page_size
指明瞭每次請求的結果數目,page
指明瞭請求的是第幾頁。另外,響應時應當保證包含total_items
與total_pages
這兩個參數,其中total_items
指示請求的集合中總的數目,total_pages
指向總的頁數(total_items
/page_size
)。
Hypermedia links用於在分頁的集合資源中指明請求其餘頁資源的便捷地址,通常來講會包含在next
, previous
, first
, last
等等相似的命名下。
若是須要根據時間進行選擇,那麼須要添加start_time
或者{property_name}_after
, end_time
或者 {property_name}_before
這些查詢參數。
sort_by
以及 sort_order
參數能夠用來指明須要被排序的資源集合。通常來講sort_by
須要包含某個獨立資源名,而sort_order
應該是asc
或者desc
值。
URI Template
GET /{version}/{namespace}/{resource}
Example Request
GET /v1/vault/credit-cards
Example Resopnse
{
"total_items": 1, "total_pages": 1, "items": [ { "id": "CARD-1SV265177X389440GKLJZIYY", "state": "ok", "payer_id": "user12345", "type": "visa", "number": "xxxxxxxxxxxx0331", "expire_month": "11", "expire_year": "2018", "first_name": "Joe", "last_name": "Shopper", "valid_until": "2017-01-12T00:00:00Z", "create_time": "2014-01-13T07:23:15Z", "update_time": "2014-01-13T07:23:15Z", "links": [ { "href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/CARD-1SV265177X389440GKLJZIYY", "rel": "self", "method": "GET" }, { "href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/CARD-1SV265177X389440GKLJZIYY", "rel": "delete", "method": "DELETE" }, { "href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/CARD-1SV265177X389440GKLJZIYY", "rel": "patch", "method": "PATCH" } ] } ], "links": [ { "href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/?page_size=10&sort_by=create_time&sort_order=asc", "rel": "first", "method": "GET" } ]
}
若是返回的資源集合爲空,即沒有任何的資源項,此時也不該該返回404 Not Found
,而應該將items
項設置爲空,而且提供一些集合的元信息,譬如total_count
設置爲0。而若是是錯誤的請求參數應當返回404 Bad Request
。不然應該返回200 OK
來表示成功的返回值。
單個資源通常比資源集合中的對應項更詳細,同時須要注意GET請求不該該影響到系統。對於敏感數據的資源標識不該該是連續的或者數值類型的,另外,若是待讀取的數據是其餘數據的子類,那麼應該使用不可變的字符串標識符,這樣可讀性與可調試性都會更好。
URI Template
GET /{version}/{namespace}/{resource}/{resource-id}
Example Request
GET /v1/vault/customers/CUSTOMER-66W27667YB813414MKQ4AKDY
Example Response
{ "merchant_customer_id": "merchant-1", "merchant_id": "target", "create_time": "2014-10-10T16:10:55Z", "update_time": "2014-10-10T16:10:55Z", "first_name": "Kartik", "last_name": "Hattangadi" }
HTTP Status
若是指定的資源並不存在,那麼應該返回404 Not Found
狀態,不然應該返回200 OK
狀態碼
注意,使用PUT動做更新單個資源的時候須要除了須要修正的值不然保證PUT請求的值與GET響應的值保持一致性,另外對於像create_time
這樣系統自動計算的值也能夠忽略。
URI Template
PUT /{version}/{namespace}/{resource}/{resource-id}
Example Request
PUT /v1/vault/customers/CUSTOMER-66W27667YB813414MKQ4AKDY { "merchant_customer_id": "merchant-1", "merchant_id": "target", "create_time": "2014-10-10T16:10:55Z", "update_time": "2014-10-10T16:10:55Z", "first_name": "Kartik", "last_name": "Hattangadi" }
HTTP Status
任何處理失敗的請求都應該返回400 Bad Request
,特別是若是客戶端想要更改某個只讀的字段,也應該返回400 Bad Request
。若是具體的業務邏輯上存在校驗規則,譬如對於數據的類型、長度等等,那麼應該提供具體的操做碼說明。若是部分場景下須要客戶單與其餘API進行交互或者在本次請求以外發出額外的請求,那麼應該返回422
狀態碼,詳情能夠參考PPaaS Blog on this topic這篇文章。
對於其餘成功的更新請求,應該返回204 No Content
狀態碼,即沒有任何的返回體。
不一樣於每次PUT請求中都須要更新資源的所有屬性,PACTH能夠根據指定的域更新對應的屬性值,而且不會影響到其餘屬性。JSON Patch 是一個推薦的信息格式,在PayPal的幾乎全部關於PATCH的操做中都有所應用。除非客戶端的特別須要,不然每次PATCH操做的返回狀態都應該是204 No Content
,這樣從帶寬的角度,特別是在移動設備中可以更好地節約流量。
URI Template
PATCH /{version}/{namespace}/{resource}/{resource-id}
Example Request
PATCH /v1/notifications/webhooks/52Y53119KP6130839 [ { "op": "replace", "path": "/url", "value": "https://www.yeowza.com/paypal_webhook_new_url" }
Example Response
204 No Content
HTTP Status
和PUT請求一致。
在刪除一個資源的時候,爲了保證客戶端的可重試性,應當將DELETE操做當作冪等操做對待。所以每次刪除操做都應該返回204 No Content
狀態碼,不然若是你返回的是404 Not Found
可能會讓客戶端誤認爲該資源是並不存在,而不是被刪除了。應該使用GET請求來驗證某個資源是否被成功刪除,而不該該經過DELETE請求進行驗證。
URI Template
DELETE /{version}/{namespace}/{resource}/{resource-id}
Example Request
DELETE /v1/vault/customers/CUSTOMER-66W27667YB813414MKQ4AKDY 204 No Content
通常來講,建立某個資源的請求體與GET/PUT不太一致,大部分狀況下API Server都會爲該資源建立一個全局的資源描述符,即便用[Create New Resource - Consumer ID]()。一旦POST請求被成功執行,也就意味着資源建立成功,那麼該資源的描述符也會被添加到資源集合的URI中。Hypermedia links提供了一種較爲便捷的方式訪問新近建立的資源,可使用rel
: self
。
URI Template
POST /{version}/{namespace}/{resource}
Example Request
POST /v1/vault/credit-cards
{
"payer_id": "user12345", "type": "visa", "number": "4417119669820331", "expire_month": "11", "expire_year": "2018", "first_name": "Betsy", "last_name": "Buyer", "billing_address": { "line1": "111 First Street", "city": "Saratoga", "country_code": "US", "state": "CA", "postal_code": "95070" }
}
Example Response
201 Created
{
"id": "CARD-1MD19612EW4364010KGFNJQI", "valid_until": "2016-05-07T00:00:00Z", "state": "ok", "payer_id": "user12345", "type": "visa", "number": "xxxxxxxxxxxx0331", "expire_month": "11", "expire_year": "2018", "first_name": "Betsy", "last_name": "Buyer", "links": [ { "href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/CARD-1MD19612EW4364010KGFNJQI", "rel": "self", "method": "GET" }, { "href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/CARD-1MD19612EW4364010KGFNJQI", "rel": "delete", "method": "DELETE" } ]
}
當某個API Consumer自定義了Resource Identifier,那麼應該使用PUT動做來建立資源,這樣也能保證冪等性。
在某些狀況下,咱們可能須要多個標識符來定位到某個資源,這一類資源每每是其餘資源的子類。
多層的資源標識符自己對於Consumer而言也是一種負擔。
儘量地將具備惟一標識符的資源或者不必指明父資源的資源做爲First-Level Resource。
要注意使用多個資源標識符的時候務必不能產生歧義,譬如/{version}/{namespace}/{resource}/{resource-id}/{sub-resource-id}
這種直接將子資源標識符放在父資源標識符以後的作法就是不合適的,會讓Consumer迷糊。
實踐中這種資源的層疊嵌套不要超過兩層。
要保證API客戶端的可用性,若是在某個URI中維持大量的層級資源標識符會大大增長複雜度。
服務端開發者須要校驗每一層級的標識符來判斷是否具備訪問權限,若是層級過深極易致使複雜度的陡升。
URI Templates
POST /{version}/{namespace}/{resource}/{resource-id}/{sub-resource} GET /{version}/{namespace}/{resource}/{resource-id}/{sub-resource} GET /{version}/{namespace}/{resource}/{resource-id}/{sub-resource}/{sub-resource-id} PUT /{version}/{namespace}/{resource}/{resource-id}/{sub-resource}/{sub-resource-id} DELETE /{version}/{namespace}/{resource}/{resource-id}/{sub-resource}/{sub-resource-id}
Examples
GET /v1/notifications/webhooks/{webhook-id}/event-types POST /v1/factory/widgets/PART-4312/sub-assemblies GET /v1/factory/widgets/PART-4312/sub-assemblies/INNER_COG PUT /v1/factory/widgets/PART-4312/sub-assemblies/INNER_COG DELETE /v1/factory/widgets/PART-4312/sub-assemblies/INNER_COG
當父子資源之間其實是一一映射的關係時,可使用單數形式的資源名來代表多個資源標識符的做用。這種狀況下子資源每每也是父資源的一部分,即所謂的被父資源全部。不然子資源應當被放置於獨立的資源集合中,而且以其餘方式代表父子資源的關聯。若是須要建立這種所謂的Singleton子資源,應該使用PUT動做,由於PUT是冪等性的。可使用PATCH來進行部分更新,不過千萬要注意不能使用PATCH進行建立操做。
URI Template
GET/PUT /{version}/{namespace}/{resource}/{resource-id}/{sub-resource}
Examples
GET /v1/customers/devices/DEV-FDU233FDSE213f)/vendor-information
URI Template
POST /{namespace}/{action-resource}
所謂複雜的操做有時候也被稱爲controller
或者actions
,務必要審慎地使用,只有在仔細考慮過上文說起的Resource Collection設計並不能知足須要的時候再進行使用。能夠參考section 2.6 of the RESTful Web Services Cookbook這一章節來了解更多的關於controller
的概念。複雜操做每每是與POST協同使用,而且大部分須要在URI中顯式地指明動做,譬如'activate', 'cancel', 'validate', 'accept', 以及 'deny'都是常見的操做。實際上,這種所謂Action-Oriented架構若是直接做用在跟URI中,即直接跟在命名空間以後,就是典型的反模式,一般這種模式較爲適用於跟隨在子資源以後。當某個場景是專一於動做,而不是資源的時候,應該建議適用Action-Oriented模式,而且此時應該適用POST動做,而後用某個單一的動詞指明action。
架構設計的可擴展性
一旦這種模式被濫用了,URI的數量會急劇增加,特別是根級別的Action能夠隨着時間瘋狂增加。一樣的這也會致使路由或者對外提供服務的配置複雜度急速增加。
URI沒法再被擴展,即不能再使用子資源。
可測試性: 由於缺少豐富的GET等讀取類操做而使得與 Resource Collection-oriented 模式相比有較大缺陷
歷史: 全部對於Action的調用應該存在某種資源中,譬如/action-resource-history
。
避免由於短暫性數據而致使資源集合模型的損害。
可用性的提高:這種Action-Oriented模式可以大大簡化客戶端交互內容,不過客戶端並不能獲益於資源自己的可讀性
與上文說起的這種單純的Action-Oriented RFC風格URL相比,更好地方法就是與Resource Collection相結合,而且使用GET /{actions}
來獲取歷史記錄。這也容許將來基於資源模型的擴展。除此以外,這種模式也能較好地與event sourcing概念相結合。
URI Template
POST /{version}/{namespace}/{action}
Example Request
POST /v1/risk/payment-decisions
{
"code": "h43j5k6iop"
}
Example Response
201 Created
{
"code": "h43j5k6iop", "status": "APPROVED", "links": [ { "href": "https://api.sandbox.paypal.com/v1/risk/payment-decisions/ID-FEF8EWR8E9FW)", "rel": "self", "method": "GET" } ]
}
不少時候咱們須要對於資源進行些特定的操做或者狀態修正,而這些操做是沒法準確的用PUT或者PATCH進行表示。這些URI看上去有點像其餘的Sub-Resources不過隱含着操做名。這個模式典型的使用場景就是當改變了某個資源的狀態以後會添加些額外的反作用。另外,每每須要將資源標識符包含在URL中。並且並非每一個Action都會修改資源的狀態。
通常來講,Action的響應狀態都是200 OK
以及資源自己,若是沒有任何的資源狀態的修正,那麼應該返回204 No Content
,而且不該該附上任何的響應體。
URI Template
POST /{version}/{namespace}/{resource}/{resource-id}/{complex-operation}
Example Request
POST /v1/payments/billing-agreements/I-0LN988D3JACS/suspend
{
"note": "Suspending the agreement."
}
Example Response
204 No Content
不過須要注意的是,雖然這種模式能夠改變狀態,也並不意味着全部的關於資源的狀態改變都要使用所謂的複雜操做模式。簡單的狀態的改變仍然可使用PUT/PATCH,也就是意味着混合使用Resource Collection與Complex Operation從而減小操做的數目。
Example Request (for mixed use of PUT)
PATCH /v1/payments/billing-agreements/I-0LN988D3JACS
[
{ "op": "replace", "path": "/", "value": { "description": "New Description", "shipping_address": { "line1": "2065 Hamilton Ave", "city": "San Jose", "state": "CA", "postal_code": "95125", "country_code": "US" } } }
]
該系列的操做每每能夠在一次請求中處理多個creates/updates/deletes操做。這一點每每從性能與可用性方面綜合考慮,這也會在影響多個資源的請求中更好地維持原子性。參考下面這個例子,capture和payment都會同時被refund操做影響。對於capture資源的PUT或者PATCH操做會隱性地影響payment資源。
URI Template
POST /{version}/{namespace}/{action}
Example Request
POST /v1/payments/captures/{capture-id}/refund
Example Response
{
"id": "0P209507D6694645N", "create_time": "2013-05-06T22:11:51Z", "update_time": "2013-05-06T22:11:51Z", "state": "completed", "amount": { "total": "110.54", "currency": "USD" }, "capture_id": "8F148933LY9388354", "parent_payment": "PAY-8PT597110X687430LKGECATA", "links": [ { "href": "https://api.sandbox.paypal.com/v1/payments/refund/0P209507D6694645N", "rel": "self", "method": "GET" }, { "href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-8PT597110X687430LKGECATA", "rel": "parent_payment", "method": "GET" }, { "href": "https://api.sandbox.paypal.com/v1/payments/capture/8F148933LY9388354", "rel": "capture", "method": "GET" } ]
}
這一個類型的複雜操做並不會保留客戶端的狀態或者建立新的資源。每每就是一個簡單的RPC調用,而後直接獲取返回值。該操做通常不會用在子資源中,由於子資源操做通常會影響父資源的狀態。此時返回是建議使用200 OK
這個狀態碼。
URI Template
POST /{version}/{namespace}/{action}
Example Request
POST /v1/risk/evaluate-payment { "code": "h43j5k6iop" }
Example Response
200 OK { "status": "VALID" }
在使用Resource Collections的時候,最好是使用查詢參數來進行集合內容的過濾。不過有時候咱們會須要一些更爲複雜的查詢語法,單純的查詢參數的方式會致使使用的問題或者受限於查詢參數的長度。這時候,應該選擇使用POST請求來指定查詢參數。
若是響應體比較大的狀況下應當使用分頁方式,須要注意的是,Consumer應該在每次子請求的時候都使用POST方式。這樣也就意味着,在POST請求體中須要維護一些查詢參數。分頁查詢參數一樣能夠參考Resource Collections這一部分。一樣能夠適用於提供 next
, previous
, first
, last
來進行其餘頁的快速讀取。
URI Template
POST /{version}/{namespace}/{search-resource}
Example Request
POST /v1/factory/widgets-search { "created_before":"1975-05-13", "status": "ACTIVE", "vendor": "Parts Inc." }
Example Response
200 OK { "items": [ <<lots of part objects here>> ] "links": [ { "href": "https://api.sandbox.factory.io/v1/factory/widgets-search?page=2&page_size=10", "rel": "next", "method": "POST" }, { "href": "https://api.sandbox.factory.io/v1/factory/widgets-search?page=124&page_size=10", "rel": "last", "method": "POST" }, ] }
在部分須要進行計算或者靜態引用的場景下,GET會比POST請求更爲合適,由於POST在HTTP層面是不會有緩存的。GET請求自己是冪等的,即不會改變資源的狀態,而POST請求能夠用於Complex Operations。
URI Template
GET /{version}/{namespace}/{read-only-resource}
Example Request
GET /v1/location/geocode?address=77+N.+Washington+Street%2C+Boston%2C+MA%2C+02114