1. 程式人生 > >來自於PayPal的RESTful API標準

來自於PayPal的RESTful API標準

之前學習過一篇文章《Google API設計指南,面向資源的設計》,有點茅塞頓開的感覺

今天又學習了一篇文章,覺得非常好,就轉載過來了,文章轉載自《來自PayPal的RestFull API標準

原文地址:PayPal API Design Guidelines


在構建API時,我們不可避免的會採用現有的跨平臺的HTTP的互動方式與資源模型,因此如果你發現你目前的模式與我們的標準南轅北轍,那麼請諮詢你們專業的API設計師以獲得進一步的建議。

URI Components

Version:版本控制

URI應當包含vN,其中N指明版本號。基於URL的版本控制相較於其他複雜的請求頭的方法會顯得簡單易用很多。

  • URI Template

/v{version}/
  • Example

/v1/

Namespaces:名稱空間

如果在URI中你需要考慮名稱空間這個概念,那麼應當選擇緊鄰在version之後的第一個欄位。名稱空間折射出消費者對於API功能的觀點,而不一定是公司本身業務邏輯層級的劃分。

  • URI Template

/{version}/{namespace}/
  • Example

/v1/vault/

Resource References:資源關聯

URI與資源之間的關聯應當保證一致性,避免出現容易引起混淆的子名稱空間或者子目錄的命名,這樣有助於使用者能夠很明晰地構造這些請求的URI。

  • URI Template

/{version}/{namespace}/{resource}/{resource-id}/{sub-resource}/{sub-resource-id}

Collection Resources

支援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   應當在多次請求下具有相同的響應

Collection Resource:獲取資源集合/列表

包含任何相關聯的元資訊的給定資源的列表,所有的資源應當包含在items域中,而類似於total_items 以及 total_pages的域指明整個關於資料整體的資訊。這種命名的一致性有助於客戶端開發者構建面向不同的資源集合的通用的處理函式。如果使用GET動作進行訪問,那麼注意不應該影響到整個系統,並且保證除非資料傳送變化否則響應訊息也應該保持一致性。另外還需要注意的是,譬如日誌輸出這種動作不會被認為是對系統的修改。
在API客戶端許可權合規的情況下允許對於資源列表進行過濾操作,即並不是本次都要把全部資源進行返回。另外,我們需要提供一個簡短的摘要性質的資源表述來減少頻寬的消耗,一般來說單個資源都包含較多的屬性。

Filtering:過濾

Paging:分頁

關於分頁的操作應該來源於請求時的pagepage_size引數,其中page_size指明瞭每次請求的結果數目,page指明瞭請求的是第幾頁。另外,響應時應當保證包含total_itemstotal_pages這兩個引數,其中total_items指示請求的集合中總的數目,total_pages指向總的頁數(total_items/page_size)。

Hypermedia links:超連結

Hypermedia links用於在分頁的集合資源中指明請求其他頁資源的便捷地址,一般來說會包含在nextpreviousfirstlast等等類似的命名下。

Time selection

如果需要根據時間進行選擇,那麼需要新增start_time 或者{property_name}_afterend_time 或者 {property_name}_before這些查詢引數。

Sorting:排序

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"
    }
]

}

HTTP Status

如果返回的資源集合為空,即沒有任何的資源項,此時也不應該返回404 Not Found,而應該將items項設定為空,並且提供一些集合的元資訊,譬如total_count設定為0。而如果是錯誤的請求引數應當返回404 Bad Request。否則應該返回200 OK來表示成功的返回值。

Read Single Resource

單個資源一般比資源集合中的對應項更詳細,同時需要注意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狀態碼

Update Single Resource

注意,使用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狀態碼,即沒有任何的返回體。

Update Partial Single Resoure

不同於每次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 Single Resource

在刪除一個資源的時候,為了保證客戶端的可重試性,應當將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

Create New Resource

一般來說,建立某個資源的請求體與GET/PUT不太一致,大部分情況下API Server都會為該資源建立一個全域性的資源描述符,即使用[Create New Resource - Consumer ID]()。一旦POST請求被成功執行,也就意味著資源建立成功,那麼該資源的描述符也會被新增到資源集合的URI中。Hypermedia links提供了一種較為便捷的方式訪問新近建立的資源,可以使用relself

  • 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"
    }
]

}

Create New Resource - Consumer Supplied Identifier

當某個API Consumer自定義了Resource Identifier,那麼應該使用PUT動作來建立資源,這樣也能保證冪等性。

Sub-Resource Collection

在某些情況下,我們可能需要多個識別符號來定位到某個資源,這一類資源往往是其他資源的子類。

Cautions

  • 多層的資源識別符號本身對於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

Sub-Resource Singleton

當父子資源之間實際上是一一對映的關係時,可以使用單數形式的資源名來表明多個資源識別符號的作用。這種情況下子資源往往也是父資源的一部分,即所謂的被父資源所有。否則子資源應當被放置於獨立的資源集合中,並且以其他方式表明父子資源的關聯。如果需要建立這種所謂的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

Complex Operation

  • 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。

Risks

  • 架構設計的可擴充套件性

    • 一旦這種模式被濫用了,URI的數量會急劇增長,特別是根級別的Action可以隨著時間瘋狂增長。同樣的這也會導致路由或者對外提供服務的配置複雜度急速增長。

    • URI無法再被擴充套件,即不能再使用子資源。

  • 可測試性: 因為缺乏豐富的GET等讀取類操作而使得與 Resource Collection-oriented 模式相比有較大缺陷

  • 歷史: 所有對於Action的呼叫應該存在某種資源中,譬如/action-resource-history

Benefits

  • 避免因為短暫性資料而導致資源集合模型的損害。

  • 可用性的提升:這種Action-Oriented模式能夠大大簡化客戶端互動內容,不過客戶端並不能獲益於資源本身的可讀性

Resource-Oriented Alternative

與上文提及的這種單純的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"
    }
]

}

Complex Operation - Sub-Resource

很多時候我們需要對於資源進行些特定的操作或者狀態修正,而