Google API Design Guide

標簽(空格分隔): google restful api design


當(dāng)前版本的API設(shè)計指南發(fā)布時間:2017-02-21

介紹

這是一份關(guān)于網(wǎng)絡(luò)API如何設(shè)計的通用指南,自2014年起Google內(nèi)部在設(shè)計Cloud API和其他Google API時一直遵循此指南。在此共享此設(shè)計指南,為外部開發(fā)人員提供指導(dǎo),以此使我們更容易協(xié)同工作。

Google Cloud Endpoints開發(fā)人員在設(shè)計gRPC API時可能會發(fā)現(xiàn)本指南特別有用,我們強烈建議此類開發(fā)人員使用這些設(shè)計原則,但是,我們也不強制要求,您可以在不遵循指南的情況下使用Cloud Endpoints和gRPC。

本指南適用于REST API和RPC API,尤其聚焦于gRPC API設(shè)計。gRPC API使用Protocol Buffers定義其API surface和使用API Service Configuration配置其API服務(wù),包括HTTP映射,logging和monitoring. Google API和Cloud Endpoints gRPC API使用HTTP映射功能進行JSON/HTTP到Protocol Buffered/RPC轉(zhuǎn)碼。

隨著新風(fēng)格和設(shè)計模式的采用和批準,本指南隨著時間推移將進行更新與補充,秉持這種精神,它永遠不是最終版,并且API設(shè)計的藝術(shù)和工藝永遠都會有提升的空間。

本文檔中使用的約定

用于此文檔的“要求級別”關(guān)鍵字,包括“必須”,“絕不”,“必需”,“應(yīng)該”,“不應(yīng)該”,“應(yīng)該”,“不應(yīng)該”,“推薦”,“可以”和“可選”,將按RFC 2119中的描述進行解釋。

在本文檔中,這些關(guān)鍵字使用粗體突出顯示。

面向資源設(shè)計

本設(shè)計指南的目標是幫助開發(fā)人員設(shè)計簡單,一致且易于使用的網(wǎng)絡(luò)API,同時覆蓋socket-based RPC APIs和 HTTP-based REST APIs。

以前,人們根據(jù)API接口和方法設(shè)計RPC API,例如CORBA和Windows COM,隨著時間的推移,系統(tǒng)引入了越來越多的接口和方法,最終結(jié)果可能對于絕大多數(shù)的接口和方法,每個接口和方法都與其他的接口和方法不同。開發(fā)人員必須仔細學(xué)習(xí)每一個,以便正確使用它,這既費時又容易出錯。

REST的架構(gòu)風(fēng)格于2000年首次被提出,主要用于與HTTP/1.1配合使用。其核心原則是定義可以被少量method操作的名稱資源,資源和method稱為API的名詞和動詞。使用HTTP協(xié)議,資源名稱自然地映射到URL,method自然地映射到HTTP方法POST,GET,PUT,PATCHDELETE。

在互聯(lián)網(wǎng),HTTP REST API取得了巨大成功。2010年,大約74%的公共網(wǎng)絡(luò)API是HTTP REST API。

然而雖然HTTP REST API在Internet上非常流行,但它們承載的流量比傳統(tǒng)的RPC API要小。例如,美國在高峰時段大約有一半的互聯(lián)網(wǎng)流量是視頻內(nèi)容,很少有人會考慮使用REST API來提供此類內(nèi)容,這是出于性能原因。在Data Center內(nèi)部,許多公司使用socket-based RPC APIs來承載大部分網(wǎng)絡(luò)流量,這可能比公共REST API高出幾個數(shù)量級。

實際上,出于各種原因我們同時需要RPC API和HTTP REST API。理想情況下,API平臺應(yīng)為所有API提供最佳支持,本設(shè)計指南通過將面向資源的設(shè)計原則應(yīng)用于通用API設(shè)計來并定義了許多常見的設(shè)計模式,以提高可用性并降低復(fù)雜性,來幫助您設(shè)計和構(gòu)建符合此原則的API。

注意:本設(shè)計指南介紹了如何將REST原則應(yīng)用于編程語言,操作系統(tǒng)或網(wǎng)絡(luò)協(xié)議無關(guān)的API設(shè)計,它不僅僅是創(chuàng)建REST API的指南。

什么是REST API

REST API被建模為可單獨尋址的資源(API的名詞)的集合。資源以其資源名稱引用,并通過一小組方法(也稱為動詞或操作)進行操作。
REST Google API的標準方法(也稱為REST方法)包括List,GetCreate,UpdateDelete。 API設(shè)計人員還可以使用自定義方法(也稱為自定義謂詞或自定義操作),以獲得無法輕松映射到標準方法之一的功能,例如數(shù)據(jù)庫事務(wù)。

注意:自定義謂詞并不意味著創(chuàng)建自定義HTTP謂詞來支持自定義方法。 對于HTTP-based APIs,它們只是映射到最合適的HTTP謂詞。

設(shè)計流程

建議在設(shè)計面向資源的API時采取以下步驟(更多細節(jié)將在下面的特定部分中介紹):

  • 確定API提供什么類型資源。
  • 確定資源之間的關(guān)系。
  • 根據(jù)類型和關(guān)系確定資源名稱方案。
  • 確定資源模式。
  • 將最少的方法集附加到資源。

Resource

面向資源API通常被建模為層次資源結(jié)構(gòu),其中每個節(jié)點是一個簡單資源或一個資源集,為簡單起見,它們通常相應(yīng)地被稱為資源和集合。

集合包含了一組相同類型的資源,例如,用戶擁有一組聯(lián)系人。資源有多個??狀態(tài)及0或多個子資源,每個子資源可以是簡單資源或資源集,例如,Gmail API有一組用戶,每個用戶都有一組消息,一組線程,一組標簽,一個配置文件和幾個設(shè)置項。

雖然存儲系統(tǒng)和REST API之間存在一些概念上的對齊,但是一個暴露了面向資源API的服務(wù)不一定必須是數(shù)據(jù)庫,并且其在解釋資源和方法方面具有極大的靈活性。例如,創(chuàng)建日歷事件(資源)可以為與會者創(chuàng)建附加事件,向與會者發(fā)送電子郵件邀請,預(yù)約會議室以及更新視頻會議時間表。

Methods

面向資源的API的關(guān)鍵特性是它強調(diào)資源(數(shù)據(jù)模型)而不是資源上執(zhí)行的方法(功能)。典型的面向資源的API使用少量方法暴露大量資源。 方法可以是標準方法或自定義方法。 對于本指南,標準方法是:List, Get, Create, Update, Delete.

若API功能能自然地映射到某個標準方法,應(yīng)該在API設(shè)計中使用該標準方法。對于那些不能自然地映射到標準方法的功能,可以使用自定義方法 ,自定義方法提供了與傳統(tǒng)RPC API相同的設(shè)計自由度,可用于實現(xiàn)常見的編程模式,例如數(shù)據(jù)庫事務(wù)或數(shù)據(jù)分析。

示例

以下部分介紹了一些關(guān)于如何將面向資源的API設(shè)計應(yīng)用于大規(guī)模服務(wù)的實際示例。您可以在 Google APIs GitHub倉庫中找到更多示例。

Gmail API

Gmail API服務(wù)實現(xiàn)了Gmail API并暴露了大部分Gmail功能,它具有以下資源模型:

  • API服務(wù):gmail.googleapis.com
  • 用戶集: users/*.每個用戶都有以下資源:
    • 消息集: users/*/messages/*.
    • 線程集: users/*/threads/*.
    • 標簽集: users/*/labels/*.
    • 變更歷史集: users/*/history/*.
    • 用戶profile: users/*/profile.
    • 用戶settings: users/*/settings.
Cloud Pub/Sub API

pubsub.googleapis.com服務(wù)實現(xiàn)了[Cloud Pub/Sub API](https://cloud.google.com/pubsub),它定義了以下資源模型:

  • API服務(wù):pubsub.googleapis.com
  • 主題集合:projects/*/topics/*
  • 訂閱集合:projects/*/subscriptions/*。

注意: 其他Pub/Sub API實現(xiàn)可能選擇不同的資源命名方案。

Cloud Spanner API

spanner.googleapis.com服務(wù)實現(xiàn)了Cloud Spanner API, 該API定義以下資源模型:

  • API服務(wù): spanner.googleapis.com
  • 實例集合: projects/*/instances/*.
    • 實例操作集合: projects/*/instances/*/operations/*.
    • 數(shù)據(jù)庫集合: projects/*/instances/*/databases/*.
    • 數(shù)據(jù)庫操作集合: projects/*/instances/*/databases/*/operations/*.
    • 數(shù)據(jù)庫會話集合: projects/*/instances/*/databases/*/sessions/*.

資源命名

在面向資源的API中,資源是命名實體,資源名稱是它們的標識符。每個資源都必須具有自己唯一的資源名稱。資源名稱由資源本身的ID、父資源的ID及其API服務(wù)名稱組成。下面我們將查看資源ID以及如何構(gòu)建資源名稱。

gRPC API應(yīng)使用scheme-less URIs作為資源名稱,它們通常遵循REST URL規(guī)范,與網(wǎng)絡(luò)文件路徑非常相似,它們可以輕松映射到REST URLs:有關(guān)詳細信息,請參閱Standard Methods部分。

集合是一種特殊的資源,包含了一組相同類型的子資源列表。例如,目錄是文件資源的集合。集合的資源ID稱為集合ID。

資源名稱使用集合ID和資源ID按層次結(jié)構(gòu)組織,以正斜杠分隔,如果資源包含子資源,則通過父資源名稱加子資源的ID來形成子資源的名稱 - 中間由正斜杠分隔。

示例1:storage服務(wù)有一組bucket,其中每個bucket 都有一組objects

API服務(wù)名稱 集合ID 資源ID 集合ID 資源ID
//storage.googleapis.com /buckets /bucket-id /objects /object-id

示例 2: email服務(wù)有一組用戶,每個用戶有一個settings子資源,settings子資源有許多其他的子資源,包括customFrom:

API服務(wù)名稱 集合ID 資源ID 集合ID 資源ID
//mail.googleapis.com /users /name@example.com /settings /customFrom

API設(shè)計人員可以使用任何合理的值作為資源或集合的ID,只要這個值在資源層次結(jié)構(gòu)中是唯一的,下面有更多指南關(guān)于如何選擇適當(dāng)?shù)馁Y源和集合ID。

假設(shè)資源名稱除分隔符外沒有包含任何其他/,通過拆分資源名稱,例如name.split("/")[n],可以獲取到單個集合ID和資源ID。

全路徑資源名稱

scheme-less URI包含DNS兼容的(DNS-compatible) API服務(wù)名稱和資源路徑,資源路徑也稱為相對資源名稱。例如:

"http://library.googleapis.com/shelves/shelf1/books/book2"

API服務(wù)名稱用于客戶端定位API服務(wù)端點,它可能是僅用于內(nèi)部服務(wù)的假DNS名稱,如果API服務(wù)名稱在上下文中很明顯,則通常使用相對資源名稱。

相對路徑資源名稱

沒有前導(dǎo)“/”的URI路徑(path-noscheme),它標識API服務(wù)中的資源,例如:

"shelves/shelf1/books/book2"

資源ID

非空URI段(segment-nz-nc)標識了父資源下的資源,參見上面示例。
跟在資源名稱后面的資源ID可能具有多個URI段。 例如:

集合ID 資源ID
files /source/py/parser.py

API服務(wù)應(yīng)盡可能使用URL友好的資源ID。無論資源ID是由客戶端還是服務(wù)器指定,都必須明確地文檔化。例如,文件名通常由客戶端分配,而電子郵件消息ID通常由服務(wù)器分配。

Collection ID

Collection ID為一個非空URI段(segment-nz-nc) ,用于標識集合中的一個資源,請參見上面示例。

由于collection IDs經(jīng)常出現(xiàn)在生成的客戶端libraries中,因此它們必須符合以下要求:

  • 必須是有效的C/C ++標識符。
  • 必須是駝峰形式的復(fù)數(shù),如果該術(shù)語沒有合適的復(fù)數(shù)形式,如“evidence”和“weather”,則使用單數(shù)形式。
  • 必須使用簡明扼要的英語術(shù)語。
  • 應(yīng)避免過于籠統(tǒng)的術(shù)語,例如:rowValues比value更好,應(yīng)無條件避免以下術(shù)語:
    • elements
    • entries
    • instances
    • items
    • objects
    • resources
    • types
    • values

Resource Name vs URL

雖然完整的Resource Name類似于一般的URL,但它們并不一樣,單個資源可以通過不同的API版本、API協(xié)議或API網(wǎng)絡(luò)端點暴露出去,完整Resource Name未包含此類信息,因此必須將其映射到特定的API版本和API協(xié)議才能實際使用。

想通過REST API使用完整Resource Name,Resource Name必須通過在服務(wù)名稱前添加HTTPS協(xié)議、在資源路徑之前添加API major version、URL轉(zhuǎn)義資源路徑來轉(zhuǎn)換為REST URL。 例如:

// This is a calendar event resource name.
"http://calendar.googleapis.com/users/john smith/events/123"

// This is the corresponding HTTP URL.
"https://calendar.googleapis.com/v3/users/john%20smith/events/123"

Resource Name as String

除非向后兼容性存在問題,否則Google API必須使用字符串表示Resource Name, 且應(yīng)該像處理普通文件路徑一樣處理Resource Name,Resource Name不支持%-encoding。

對于資源定義,第一個字段應(yīng)該是稱為name的一個字符串,用于表示資源名稱。
注意:應(yīng)限定其他與name相關(guān)的字段以避免混淆,例如display_name,first_name,last_name,full_name。
例如:

service LibraryService {
  rpc GetBook(GetBookRequest) returns (Book) {
    option (google.api.http) = {
      get: "/v1/{name=shelves/*/books/*}"
    };
  };
  rpc CreateBook(CreateBookRequest) returns (Book) {
    option (google.api.http) = {
      post: "/v1/{parent=shelves/*}/books"
      body: "book"
    };
  };
}

message Book {
  // Resource name of the book. It must have the format of "shelves/*/books/*".
  // For example: "shelves/shelf1/books/book2".
  string name = 1;

  // ... other properties
}

message GetBookRequest {
  // Resource name of a book. For example: "shelves/shelf1/books/book2".
  string name = 1;
}

message CreateBookRequest {
  // Resource name of the parent resource where to create the book.
  // For example: "shelves/shelf1".
  string parent = 1;
  // The Book resource to be created. Client must not set the `Book.name` field.
  Book book = 2;
}

注意:為了保證資源名稱的一致性,任何URL模板變量都不能捕獲前導(dǎo)/,例如,必須使用URL模板/v1/{name=shelves/*/books/*}而非/v1{name=/shelves/*/books/*}.

問題

Q: 為什么不使用資源ID來識別資源?

A:任何大型系統(tǒng),都包含多種資源,若使用資源ID來標識資源,我們需要根據(jù)每個資源指定元組來標識,例如(bucket,object)(user,album,photo),它會產(chǎn)生如下幾個問題:

  • 開發(fā)人員必須了解并記住這些匿名元組。
  • 傳遞元組通常比傳遞字符串更難。
  • 集中式基礎(chǔ)架構(gòu)(如日志記錄和訪問控制系統(tǒng)),不能識別專門的元組。
  • 專門的元組限制了API設(shè)計的靈活性,例如提供可重用的API接口。 例如, Long Running Operations 可以與許多其他API接口一起使用,因為它們使用了靈活的資源名稱。
Q: 為什么特殊域叫做name而不是id?

因為資源“名稱”概念,所以特殊域叫name,一般來說,我們發(fā)現(xiàn)name概念讓開發(fā)人員感到困惑,例如,文件名實際上只是文件名稱還是文件的完整路徑?通過保留標準字段name,開發(fā)人員不得不選擇更合適的術(shù)語,例如display_nametitlefull_name

標準方法

本章定義了標準方法的概念,即List, Get, Create, Update, 和 Delete. 標準方法可降低復(fù)雜性并提高一致性。 Google APIs repository 中超過70%的API方法都是標準方法,這使得它們更易于學(xué)習(xí)和使用。

下表描述了如何將標準方法映射為HTTP方法:

Standard Method HTTP Mapping HTTP Request Body HTTP Response Body
List GET <collection URL> N/A Resource* list
Get GET <resource URL> N/A Resource*
Create POST <collection URL> Resource Resource*
Update PUT or PATCH <resource URL> Resource Resource*
Delete DELETE <resource URL> N/A google.protobuf.Empty**

如果方法支持響應(yīng)字段掩碼且指定了要返回的字段子集,List, Get, Create, 和Update方法返回的資源可能包含部分數(shù)據(jù)。在某些情況下,API平臺本身支持所有方法的字段掩碼。

不立即刪除資源的Delete方法(如只是更新狀態(tài)標志或創(chuàng)建一個long-running的刪除操作)返回的響應(yīng)應(yīng)包含這個long-running操作或修改后的資源。

對于在單個API調(diào)用時間跨度內(nèi)未完成的請求,標準方法還可以返回一個 long running operation 。

以下部分詳細描述了每種標準方法,這些示例顯示了.proto文件中定義的方法,其中包含HTTP映射的特殊注釋。 您可以在 Google APIs repository中找到許多使用標準方法的示例。

List

List方法傳遞collection名稱和0或多個其他參數(shù)作為輸入,并返回與輸入匹配的資源列表。

List通常用于資源搜索,適用于搜索單個集合的數(shù)據(jù),該集合大小有限且未緩存。對于更廣泛的情況,應(yīng)使用custom method Search。

批量獲?。ɡ?,一個方法接收多個資源ID值且為每個ID返回對應(yīng)的對象)應(yīng)該通過自定義BatchGet方法來實現(xiàn),而非List。但是,如果您已提供相同功能的List方法,則可以重用List方法。如果您使用的是自定義BatchGet方法,則應(yīng)將其映射到HTTP GET。

適用的常見模式: pagination, result ordering.

適用的命名約定:filter field, results field

HTTP映射:

  • List方法必須使用HTTP GET動詞。
  • 請求消息字段中的集合名稱(資源列表名稱),必須映射到URL路徑上,如果集合名稱映射到URL路徑,則URL模板的最后一段(the collection ID)必須是字面常量。
  • 所有剩下的請求消息字段應(yīng)映射到URL查詢參數(shù)。
  • 沒有請求body,API配置不得聲明body條款。
  • 響應(yīng)body應(yīng)包含資源列表以及可選元數(shù)據(jù)。

例:

// Lists books in a shelf.
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {
  // List method maps to HTTP GET.
  option (google.api.http) = {
    // The `parent` captures the parent resource name, such as "shelves/shelf1".
    get: "/v1/{parent=shelves/*}/books"
  };
}

message ListBooksRequest {
  // The parent resource name, for example, "shelves/shelf1".
  string parent = 1;

  // The maximum number of items to return.
  int32 page_size = 2;

  // The next_page_token value returned from a previous List request, if any.
  string page_token = 3;
}

message ListBooksResponse {
  // The field name should match the noun "books" in the method name.  There
  // will be a maximum number of items returned based on the page_size field
  // in the request.
  repeated Book books = 1;

  // Token to retrieve the next page of results, or empty if there are no
  // more results in the list.
  string next_page_token = 2;
}

Get

Get方法傳遞資源名稱,0或多個其他參數(shù),并返回對應(yīng)的資源對象。

HTTP映射:

  • Get方法必須使用HTTP GET動詞。
  • 請求消息字段中的資源名稱應(yīng)映射到URL路徑。
  • 所有剩下的請求消息字段應(yīng)映射到URL查詢參數(shù)。
  • 沒有請求body, API配置不得聲明body條款。
  • 響應(yīng)body即為返回的資源。

例:

// Gets a book.
rpc GetBook(GetBookRequest) returns (Book) {
  // Get maps to HTTP GET. Resource name is mapped to the URL. No body.
  option (google.api.http) = {
    // Note the URL template variable which captures the multi-segment resource
    // name of the requested book, such as "shelves/shelf1/books/book2"
    get: "/v1/{name=shelves/*/books/*}"
  };
}

message GetBookRequest {
  // The field will contain name of the resource requested, for example:
  // "shelves/shelf1/books/book2"
  string name = 1;
}

Create

Create方法傳遞父資源名稱,資源以及0或多個其他參數(shù),在指定的父資源下創(chuàng)建新資源,并返回新創(chuàng)建的資源。

如果API支持創(chuàng)建資源,則應(yīng)該為每一種可創(chuàng)建的資源新增一個Create方法。

HTTP映射:

  • Create方法必須使用HTTP POST動詞。
  • 請求消息應(yīng)包含父資源名稱,在該父資源下創(chuàng)建資源。
  • 所有剩下的請求消息字段應(yīng)映射到URL查詢參數(shù)。
  • 該請求可能包含名為<resource>_id的字段,以允許調(diào)用者傳客戶端選擇分配的ID,該字段必須映射到URL查詢參數(shù)。
  • 請求消息字段'資源'應(yīng)映射到請求body,如果Create方法使用了body HTTP配置條款,必須使用body: <resource_field>形式。
  • 響應(yīng)body即為返回的資源。

如果Create方法支持客戶端分配資源名稱,且資源已存在,請求應(yīng)該失敗并返回錯誤代碼ALREADY_EXISTS,或使用由服務(wù)器分配的不同的資源名稱,并且文檔應(yīng)該清楚說明創(chuàng)建的資源名稱可能與傳入的不同。

Create方法必須傳遞使用輸入資源,以便在資源架構(gòu)更改時,無需更新請求架構(gòu)和資源架構(gòu)。對于客戶端無法設(shè)置的資源字段,必須將它們記錄為“Output only”。

The Create method must take an input resource, so that when the resource schema changes, there is no need to update both request schema and resource schema. For resource fields that cannot be set by the clients, they must be documented as "Output only" fields.

// Creates a book in a shelf.
rpc CreateBook(CreateBookRequest) returns (Book) {
  // Create maps to HTTP POST. URL path as the collection name.
  // HTTP request body contains the resource.
  option (google.api.http) = {
    // The `parent` captures the parent resource name, such as "shelves/1".
    post: "/v1/{parent=shelves/*}/books"
    body: "book"
  };
}

message CreateBookRequest {
  // The parent resource name where the book to be created.
  string parent = 1;

  // The book id to use for this book.
  string book_id = 3;

  // The book resource to create.
  // The field name should match the Noun in the method name.
  Book book = 2;
}

rpc CreateShelf(CreateShelfRequest) returns (Shelf) {
  option (google.api.http) = {
    post: "/v1/shelves"
    body: "shelf"
  };
}

message CreateShelfRequest {
  Shelf shelf = 1;
}

Update

Update方法接收包含了資源、0或多個參數(shù)的request消息,它更新資源及其屬性,并返回更新后的資源對象。

除了資源名稱或其父級之外,可變資源屬性應(yīng)該都能被Update方法更新,任何對資源進行renamemove的操作都不能出現(xiàn)在Update方法中,而應(yīng)由自定義方法處理。

HTTP映射:

  • 標準Update方法應(yīng)支持只對資源部分屬性進行更新,使用HTTP PATCH與名為update_maskFieldMask屬性一起實現(xiàn)(patch只更新傳入的屬性,原有屬性不變,put一般更新除ID外的整個對象,對象不存在,patch能創(chuàng)建新對象,put不能,put冪等,Post每次創(chuàng)建)。
  • 需要更加高級patching語義的Update方法(例如附加到重復(fù)字段),應(yīng)該自定義方法.實現(xiàn)。
  • 如果Update方法僅支持全部的資源更新,必須使用HTTP PUT,但是,強烈建議不要進行全更新,因為新增資源字段時會出現(xiàn)向后兼容問題。
  • 資源名稱字段必須映射到URL路徑,該字段也可能同時在資源消息體中。
  • 資源對象必須置于請求body。
  • 所有其他的請求消息字段必須映射到URL查詢參數(shù)(?xxx=xxx&)。
  • 響應(yīng)消息必須是更新后的資源。

如果API接受客戶端指定的資源名稱,則服務(wù)端可以允許客戶端指定不存在的資源名稱并創(chuàng)建新資源,否則,對于不存在的資源名稱,Update方法必須失敗,且返回錯誤代碼NOT_FOUND

若API具有支持資源創(chuàng)建的Update方法,還應(yīng)提供Create方法,如果Update方法是唯一的創(chuàng)建資源方法,調(diào)用方會不清楚如何創(chuàng)建資源。

例:

// Updates a book.
rpc UpdateBook(UpdateBookRequest) returns (Book) {
  // Update maps to HTTP PATCH. Resource name is mapped to a URL path.
  // Resource is contained in the HTTP request body.
  option (google.api.http) = {
    // Note the URL template variable which captures the resource name of the
    // book to update.
    patch: "/v1/{book.name=shelves/*/books/*}"
    body: "book"
  };
}

message UpdateBookRequest {
  // The book resource which replaces the resource on the server.
  Book book = 1;

  // The update mask applies to the resource. For the `FieldMask` definition,
  // see https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask
  FieldMask update_mask = 2;
}

Delete

Delete方法接收資源名稱、0或多個參數(shù),刪除或計劃刪除(即只是下發(fā)刪除指令,具體什么時候刪除由server端具體實現(xiàn))指定的資源,Delete方法應(yīng)返回google.protobuf.Empty。

API調(diào)用方不應(yīng)該依賴于Delete方法返回的任何信息,因為它不能重復(fù)調(diào)用。

HTTP映射:

  • Delete方法必須使用HTTP DELETE謂詞。
  • 資源名稱字段應(yīng)映射到URL路徑。
  • 所有其他請求字段應(yīng)映射到URL query參數(shù)。
  • 沒有請求body, API配置不得聲明body條款。
  • 如果Delete方法立即刪除資源,則應(yīng)返回空響應(yīng)。
  • 如果Delete方法啟動long-running操作,它應(yīng)該返回long-running操作。
  • 如果Delete方法僅將資源標記為已刪除,則應(yīng)返回更新后的資源。

Delete方法的調(diào)用應(yīng)該是冪等的,但不必產(chǎn)生相同的響應(yīng),任意次的Delete請求都應(yīng)該導(dǎo)致(最終)能刪除資源,但只有第一次請求才會返回成功碼,后續(xù)請求應(yīng)返回google.rpc.Code.NOT_FOUND。

// Deletes a book.
rpc DeleteBook(DeleteBookRequest) returns (google.protobuf.Empty) {
  // Delete maps to HTTP DELETE. Resource name maps to the URL path.
  // There is no request body.
  option (google.api.http) = {
    // Note the URL template variable capturing the multi-segment name of the
    // book resource to be deleted, such as "shelves/shelf1/books/book2"
    delete: "/v1/{name=shelves/*/books/*}"
  };
}

message DeleteBookRequest {
  // The resource name of the book to be deleted, for example:
  // "shelves/shelf1/books/book2"
  string name = 1;
}

自定義方法

本章將討論API設(shè)計時如何使用自定義方法。

自定義方法應(yīng)只應(yīng)用于通過標準方法無法輕易表達的功能, 通常,API設(shè)計者應(yīng)盡可能選擇標準方法而不是自定義方法。標準方法具有大多數(shù)開發(fā)人員熟悉的更簡單且定義良好的語義,因此它們更易于使用且不易出錯。 標準方法的另一個優(yōu)點是API平臺能更好地理解和支持標準方法,例如計費,錯誤處理,日志記錄,監(jiān)控。

自定義方法可以與一個資源,一個集合或一個服務(wù)相關(guān)聯(lián),它可能需要任意請求并返回任意響應(yīng),并且還支持流請求和響應(yīng)。

自定義方法名稱必須遵循 method naming conventions.

HTTP映射

對于自定義方法,必須使用以下通用HTTP映射:
https://service.name/v1/some/resource/name:customVerb
使用:代替/來分割自定義謂詞與資源名稱的原因是,為了支持任意類型路徑,undelete一個文件可以映射為POST /files/a/long/file/name:undelete

選擇HTTP映射時,應(yīng)遵循以下準則:

  • 自定義方法應(yīng)該使用HTTP POST,因為它具有最靈活的語義,但用于替代get或list的方法除外,它們可能使用GET。(詳見第三章)
  • 自定義方法不應(yīng)使用HTTP PATCH,但可以使用其他HTTP謂詞,在這種情況下,方法必須遵循該動詞的標準HTTP語義。
  • 值得注意的是,使用HTTP GET的自定義方法必須是冪等的,沒有副作用。例如,在資源上實現(xiàn)特殊視圖的自定義方法,應(yīng)使用HTTP GET。
  • 資源或集合的資源名稱字段應(yīng)映射到URL路徑。
  • URL路徑必須以冒號后跟custom verb的后綴結(jié)尾。
  • 如果用于自定義方法的HTTP謂詞允許HTTP請求body(POST,PUTPATCH或自定義HTTP謂詞),則此類自定義方法的HTTP配置必須使用body:“*”條款,所有其他的請求消息字段應(yīng)映射到HTTP請求body。
  • 如果用于自定義方法的HTTP謂詞不接受HTTP請求body(GET,DELETE),則此方法的HTTP配置不能使用body條款,所有剩余的請求消息字段都應(yīng)映射到URL查詢參數(shù)。

WARNING: 如果一個service實現(xiàn)了多個API,API生產(chǎn)者必須非常小心創(chuàng)建服務(wù)配置,以避免API之間的自定義動詞沖突。

// This is a service level custom method.
rpc Watch(WatchRequest) returns (WatchResponse) {
  // Custom method maps to HTTP POST. All request parameters go into body.
  option (google.api.http) = {
    post: "/v1:watch"
    body: "*"
  };
}

// This is a collection level custom method.
rpc ClearEvents(ClearEventsRequest) returns (ClearEventsResponse) {
  option (google.api.http) = {
    post: "/v3/events:clear"
    body: "*"
  };
}

// This is a resource level custom method.
rpc CancelEvent(CancelEventRequest) returns (CancelEventResponse) {
  option (google.api.http) = {
    post: "/v3/{name=events/*}:cancel"
    body: "*"
  };
}

// This is a batch get custom method.
rpc BatchGetEvents(BatchGetEventsRequest) returns (BatchGetEventsResponse) {
  // The batch get method maps to HTTP GET verb.
  option (google.api.http) = {
    get: "/v3/events:batchGet"
  };
}

Use Cases

一些特殊場景,自定義方法可能是正確選擇:

  • 重啟虛擬機.設(shè)計備選方案可能是“在reboots集合中創(chuàng)建reboot 資源”,感覺極其復(fù)雜,或者“虛擬機具有可變狀態(tài),客戶端可以將其從RUNNING更新到RESTARTING”,這將引入還可能有哪些狀態(tài)間轉(zhuǎn)換的問題。此外,重啟是一個眾所周知的概念,可以很好地轉(zhuǎn)換為直觀的且滿足開發(fā)人員期望的自定義??方法。
  • 發(fā)送郵件.創(chuàng)建電子郵件不一定要發(fā)送(草稿),與設(shè)計替代方案(將消息移動到“Outbox”集合)相比,自定義方法具有更易被API用戶理解且能更直觀對概念進行建模的優(yōu)點。
  • 晉升員工.如果以標準update實現(xiàn),client端必須復(fù)制企業(yè)管理晉升流程的政策,以確保晉升在同一職業(yè)階梯內(nèi)達到正確的級別。
  • 批處理方法.對于性能關(guān)鍵方法,提供自定義批處理方法以減少每個請求的開銷可能很有用。例如,accounts.locations.batchGet.

一些標準方法比自定義方法更合適的示例:

  • 使用不同的查詢參數(shù)查詢資源(使用帶有標準列表過濾的標準list方法)。
  • 簡單的資源屬性更改(使用帶字段掩碼的標準update方法)。
  • 關(guān)閉通知(使用標準delete方法)。

通用自定義方法

下面列出了常用或有用的自定義方法名稱的精選列表。 API設(shè)計者在引入自己的名稱之前應(yīng)考慮這些名稱,以促進API之間的一致性。

Method Name Custom verb HTTP verb Note
Cancel :cancel POST 取消未完成的操作 (構(gòu)建,計算等.)
BatchGet<復(fù)數(shù)名詞> :batchGet GET 批量獲取多個資源. (見詳情 the description of List)
Move :move POST 將資源從一個父節(jié)點移動到另一個
Search :search GET List的替代方法,用于獲取不符合List語義的數(shù)據(jù)。
Undelete :undelete POST .還原以前刪除的資源, 建議的保留期為30天。

標準字段

本節(jié)描述了一組標準的消息字段定義,供具有類似概念時使用,以確保相同的概念在不同的API中具有相同的名稱和語義。

Name Type Description
name string name字段應(yīng)包含相對資源名稱.
parent string 對于資源定義和List/Create請求,parent字段應(yīng)包含父級相對資源名稱.
create_time Timestamp 實體創(chuàng)建時間
update_time Timestamp 實體最近更新時間. 注意: 當(dāng)執(zhí)行create/patch/delete操作時需要更新update_time.
delete_time Timestamp 實體的刪除時間戳,僅當(dāng)邏輯刪除時.
expire_time Timestamp 實體到期時的時間戳
start_time Timestamp 標記某個時間段開始的時間戳
end_time Timestamp 標記某個時間段或操作結(jié)束的時間戳(無論其成功與否)
read_time Timestamp 特定實體應(yīng)讀?。ㄈ绻谡埱笾惺褂茫┗蛞炎x取(如果在響應(yīng)中使用)的時間戳
time_zone string 時區(qū)名稱. 是一個 IANA TZ 名稱,如"America/Los_Angeles". 更多信息參考 https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
region_code string 位置的Unicode國家/地區(qū)代碼(CLDR),如"US"和"419". 更多信息參考http://www.unicode.org/reports/tr35/#unicode_region_subtag.
language_code string BCP-47語言代碼, 如"en-US"或"sr-Latn". 更多信息參考http://www.unicode.org/reports/tr35/#Unicode_locale_identifier.
mime_type string IANA發(fā)布的MIME類型(也稱為media type). 更多信息參考https://www.iana.org/assignments/media-types/media-types.xhtml.
display_name string 實體的顯示名稱
title string 實體的正式名稱,例如公司名稱,被視為display_name的正式版本
description string 實體的一個或多個文本描述段落
filter string List方法的標準過濾器參數(shù)
query string 如果應(yīng)用于搜索方法(ie :search),則與“filter”相同
page_token string List請求中的分頁標記
page_size int32 List請求中的分頁大小
total_size int32 列表中的記錄總數(shù),與分頁無關(guān)
next_page_token string List response中的下一分頁標記,作為下一頁請求的page_token的值, 空值表示當(dāng)前為最后一頁。
order_by string 指定List請求的結(jié)果如何排序
request_id string 用于檢測重復(fù)請求的字符串,具有唯一性
resume_token string 用于恢復(fù)流式傳輸請求的不透明令牌
labels map<string,string> 表示云資源標簽
deleted bool 如果資源允許取消刪除行為,則必須具有 deleted字段,標示資源已刪除。
show_deleted bool 如果資源允許取消刪除行為,則相應(yīng)的List方法必須具有show_deleted字段,以便客戶端可以發(fā)現(xiàn)已刪除的資源。
update_mask FieldMask 它用于Update請求消息,用于對資源部分更新。此掩碼與資源相關(guān),而不是與請求消息相關(guān)
validate_only bool 如果為true,則表示僅應(yīng)驗證請求,而不執(zhí)行

Errors

本章概述了Google APIs錯誤模型以及提供了開發(fā)人員如何正確生成和處理錯誤的通用指導(dǎo)。

Google APIs使用簡單的協(xié)議無關(guān)的錯誤模型,它允許我們跨不同的APIs、API協(xié)議(如gRPC或HTTP)和錯誤上下文(例如,異步,批處理或工作流錯誤)暴露一致的體驗。

Error Model

錯誤模型由google.rpc.Status邏輯定義,當(dāng)發(fā)生API錯誤時,將返回給客戶端錯誤模型實例。以下代碼片段示范了錯誤模型的整體設(shè)計:

package google.rpc;

message Status {
  // A simple error code that can be easily handled by the client. The
  // actual error code is defined by `google.rpc.Code`.
  int32 code = 1;

  // A developer-facing human-readable error message in English. It should
  // both explain the error and offer an actionable resolution to it.
  string message = 2;

  // Additional error information that the client code can use to handle
  // the error, such as retry delay or a help link.
  repeated google.protobuf.Any details = 3;
}

由于大多數(shù)Google APIs使用resource-oriented的設(shè)計方式,因此錯誤處理遵循同樣的設(shè)計原則,對大量資源使用一小組標準錯誤。例如,server端使用一個標準的google.rpc.Code.NOT_FOUND 錯誤碼、而非定義不同類型的“not found”錯誤來告訴客戶端找不到哪個特定資源。較小的狀態(tài)空間降低了文檔的復(fù)雜性,在客戶端庫中提供了更好的慣用映射,并降低了客戶端邏輯復(fù)雜性,同時不限制包含actionable信息。

Error Codes

Google API 必須使用由google.rpc.Code定義的規(guī)范錯誤碼。單個API 應(yīng)該避免定義其他錯誤代碼,因為開發(fā)人員不太可能編寫邏輯來處理大量錯誤碼。作為參照,若每個API調(diào)用平均處理3個錯誤碼,那意味著大部分應(yīng)用邏輯只是用于錯誤處理,這不是一個好的開發(fā)體驗。

Error Messages

錯誤消息應(yīng)該可以幫助用戶輕松快速地理解和解決API錯誤。通常,在編寫錯誤消息時請考慮以下準則:

  • 不要假設(shè)每個用戶都是您API的專家用戶,用戶可能是client開發(fā)人員,運維人員,IT staff甚至應(yīng)用終端用戶。
  • 不要假設(shè)用戶對您的服務(wù)實現(xiàn)細節(jié)了如指掌,或者熟悉錯誤的上下文(例如日志分析)。
  • 如果可能,構(gòu)建的錯誤消息應(yīng)能幫技術(shù)用戶(但不一定是API的開發(fā)人員)響應(yīng)錯誤并修正錯誤。
  • 保持錯誤消息簡潔。如果有必要,提供一個鏈接,讓對錯誤困惑的人可以提出問題、提交反饋或獲取錯誤消息的更詳細的信息,否則,使用詳細信息字段進行擴展。

Error Details

Google APIs為錯誤詳情定義了一組標準錯誤payloads(有效負載),您可以在google/rpc/error_details.proto.中找到相關(guān)信息。這些內(nèi)容涵蓋了API錯誤最常見的需求,例如配額失敗和無效參數(shù)。與錯誤代碼一樣,錯誤詳情應(yīng)盡可能使用這些標準有效負載。

除非可以幫助應(yīng)用代碼處理error,否則不要引入其他錯誤詳情類型。如果錯誤只能靠人工依賴錯誤消息內(nèi)容進行處理,那就讓開發(fā)人員手動處理,而不是引入新的錯誤詳情類型。

以下是一些error_details 有效負載示例:

  • RetryInfo 描述客戶端何時可以重試失敗(可能返回了Code.UNAVAILABLECode.ABORTED)的請求
  • QuotaFailure 描述了配額檢查失敗(可能返回了Code.RESOURCE_EXHAUSTED)原因
  • BadRequest描述了客戶端違規(guī)請求,可能返回了Code.INVALID_ARGUMENT

HTTP Mapping

雖然proto3消息模塊具有原生JSON編碼,Google API平臺為Google JSON REST APIs使用了不同的錯誤Schema,以實現(xiàn)向后兼容。

Schema:

// The error schema for Google REST APIs. NOTE: this schema is not used for
// other wire protocols.
message Error {
  // This message has the same semantics as `google.rpc.Status`. It has an extra
  // field `status` for backward compatibility with Google API Client Library.
  message Status {
    // This corresponds to `google.rpc.Status.code`.
    int32 code = 1;
    // This corresponds to `google.rpc.Status.message`.
    string message = 2;
    // This is the enum version for `google.rpc.Status.code`.
    google.rpc.Code status = 4;
    // This corresponds to `google.rpc.Status.details`.
    repeated google.protobuf.Any details = 5;
  }
  // The actual error payload. The nested message structure is for backward
  // compatibility with Google API client libraries. It also makes the error
  // more readable to developers.
  Status error = 1;
}

示例:

{
  "error": {
    "code": 401,
    "message": "Request had invalid credentials.",
    "status": "UNAUTHENTICATED",
    "details": [{
      "@type": "type.googleapis.com/google.rpc.RetryInfo",
      ...
    }]
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 去年有段時間得空,就把谷歌GAE的API權(quán)威指南看了一遍,收獲頗豐,特別是在自己幾乎獨立開發(fā)了公司的云數(shù)據(jù)中心之后...
    騎單車的勛爵閱讀 21,116評論 0 41
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,628評論 19 139
  • 一說到REST,我想大家的第一反應(yīng)就是“啊,就是那種前后臺通信方式?!钡窃谝笤敿氈v述它所提出的各個約束,以及如...
    時待吾閱讀 3,601評論 0 19
  • feisky云計算、虛擬化與Linux技術(shù)筆記posts - 1014, comments - 298, trac...
    不排版閱讀 4,356評論 0 5
  • 馬蹄聲聲風(fēng)雨急, 舞葉飛花摧人意。 忽如利箭天外來, 千支萬支入湖底。
    陳謙swan閱讀 486評論 0 1

友情鏈接更多精彩內(nèi)容