1. 程式人生 > >RESTful API設計系列三:資源

RESTful API設計系列三:資源

說明

資源是任何RESTful API中的基本概念。資源是物件,包括型別、關聯的資料、資源間的關係以及資源上的操作集合。它和麵向物件程式語言中的物件類似,不同點在於資源
只定義了有限的標準方法(對應HTTP協議中標準的GET,POST,PUT,DELETE方法),而物件例項可以有很多方法。

資源可以被分類到不同的集合中。每個集合都包含一種型別的資源,因此集合都是均勻(homogeneous)的。資源也可以不分到集合裡,這些資源我們稱為singleton resources

集合可以全域性存在,即API頂層(譯者注:原文at the top level of an API),也可以包含在single resource中。後文中,我們把這種集合稱為sub-collections

,子集通常用來表達“包含於”的關係。我會在Relationships詳細介紹。

下圖描述了RESTful API的關鍵概念:

我們把描述資源的型別、行為和關係的資訊稱為API的資源模型。RESTful中的資源模型可以視為到應用資料模型的對映。

資源資料

資源關聯資料。API的資源模型還包括關聯資料的豐富性。比如,它定義了哪些可用的資料型別和行為。

就我個人經驗,我堅信JSON這種資料模型完美滿足API的豐富性要求,它是RESTful資源的理想資料模型。我會推薦給每個人。

JSON中已經存在三種類型資料:

  • scalar(標量:number, string, boolean, nul)
  • array
  • object

標量型別只有一個值。陣列包含任意型別值的有序列表。物件是無序的key/value(鍵/值對)集合(亦稱為屬性,但是不要和XML中的屬性概念搞混),key是字串,value可以是任意型別。更多JSON細節請參考JSON web site

為什麼如此偏愛JSON?以我個人觀點,JSON在表達性和廣泛應用上擁有良好的平衡。scalars、arrays和objects這三種類型足夠強大,能夠以自然的方式描述資源中暴露的資料,同時JSON足夠小巧,幾乎任何現代語言都可以內建支援JSON。

XML也是以為有力的競爭者(contender)。實際上,RHEV-M(譯者注:紅帽的一款產品)最終API中就使用XMLSchema來描述資源。事後來看(With hindsight),RESTful API使用XML模型是個糟糕的選擇。一方面,它過於豐富;另一方面,它又缺少一些特性。XML作為標準通用標記語言的一個分支(SGML off-shoot),我認為它在表示結構化文件

是偉大的,但是不適合表示結構化資料

XML一些過於豐富的特性有:

  • Attributes vs elements(屬性與元素)。XML可以既有屬性,也包含子元素。包含資料項的資源可以被編碼成任意一種。這導致客戶端或者服務端事先不清楚該使用哪一個。
  • Relevance of order(順序相關性)。子元素間的順序也會關聯到XML中,我認為物件間的屬性就不是自然的有序了

XML資料模型的缺點有:

  • 沒有型別。XML文件中的元素沒有型別,為了使用型別需要引入XMLSchema,不幸的是XMLSchema規範非常複雜。
  • 沒有列表。XML不能原生表達列表。這可能導致問題:不清楚某個元素是列表還是物件,或者兩者都是。

應用資料

我們使用以下規則定義可以與JSON資料模型對映的資源資料:

  1. 資源被建模為JSON物件。資源的型別儲存在特殊的鍵值對_type中。
  2. 資源中的資料表示為JSON物件中的鍵值對。為了避免和JSON物件內部鍵值對衝突,鍵不能以“_”開頭
  3. 鍵值對中的值可以是JSON中任意原生型別: string、number、boolean、null或者arrays。值還可以是物件,這種情況下值表示巢狀的資源。
  4. 集合表示成物件陣列。

我們也會把鍵值對認為JSON物件中的屬性,這裡不詳細描述區別,都使用統一的術語。這樣JSON中的屬性就不會和XML中的屬性衝突了。

REST元資料

除了暴露應用資料,資源中還有RESTful API相關特殊的資料。這些資訊包括URLs和relationships。

下表列出了所有資源中定義的,通用、有特殊含義的屬性:

Attribute Type Meaning
id String Identifies the unique ID of a resource
href String Identifies the URL of the current resource
link object Identifies a relationship for a resource. This attribute is itself an object and has “rel” “href” attributes

其它資料

通常,除了應用資料、REST元資料外,我們還需要一些資料。這通常是“類RPC”資料,其中需要設定操作,但是設定最終不會作為資源本身一部分。

這裡我能列舉的例子是,建立新資源過程中需要引用另一個資源,但是被引用的資源最終不會成為建立資源的一部分。

將應用資料、REST元資料和其它資料合併到資源中是API程式碼的職責,有可能要解決可能出現的名稱衝突的問題。

表示

我們已經定義了資源,同時也介紹了資源資料和JSON資料模型間的對映關係。但是,這些資源仍然是抽象的實體。在它們通過HTTP連結和客戶端通訊前,它們需要被序列化成文字表現形示。然後這種文字表示就可以作為實體包含在HTTP訊息體中。

以下表示型別是資源常用的,該表還可使用的內容型別:

Type Content-Type
JSON application/x-resource+json application/x-collection+json
YAML application/x-resource+yaml application/x-collection+yaml
XML application/x-resource+xml application/x-collection+xml
HTML text/html

注意:所有使用x-字首的內容型別都是實驗階段,RFC2046也是認可的。

JSON格式

將資源序列化為JSON格式很簡單,因為資源的資料模型是根據JSON模型定義的。下面我們給出一個虛擬機器JSON序列化的例子:

{
  "_type": "vm",
  "name": "A virtual machine",
  "memory": 1024,
  "cpu": {
    "cores": 4,
    "speed": 3600
  },
  "boot": {
    "devices": ["cdrom", "harddisk"]
  }
}

YAML格式

YAML格式和JSON稍微不同,JSON中鍵值對裡的“_type”在YAML中替換為“!type”註解。上面的虛擬機器例項的YAML格式為:

!vm
name: A virtual machine
memory: 1024
cpu:
  cores: 4
  speed: 3600
boot:
  devices:
  - cdrom
  - harddisk

XML格式

由於XML的複雜性和限制,XML表示法是最複雜的。我推薦下面的規則:

  • 資源對映到XML元素,加上標籤名錶示資源型別。
  • 資源屬性對映到XML子元素,標籤名錶示屬性名。
  • 標量表示成文字節點。標量元素中關鍵字“type”表示標量型別,這種對映要遵守XML Schema Part 2
  • 列表要儲存為單個的容器元素,其中每個列表項都有子元素。容器元素的標籤應當是屬性名稱英文複數,item標籤應該是屬性名稱的英文單數。列表應該具有“xd:list”型別註釋。

相同的虛擬機器資源的XML表示格式為:

<vm xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <name type="xs:string">My VM</name>
  <memory type="xs:int">1024</memory>
  <cpu>
    <cores type="xs:int">4</cores>
    <speed type="xs:int">3600</speed>
  </cpu>
  <boot>
    <devices type="xs:list">
      <device type="xs:string">cdrom</device>
      <device type="xs:string">harddisk</device>
    </devices>
  </boot>
</vm>

HTML格式

HTML響應的精確格式應該是API相關的。HTML是為人類使用設計的,因此唯一要求是易於理解。一個簡單實現可以是下面的表示法:

  • 對於集合,使用<table>標籤表示,每一列表示一個屬性,每一行表示一個物件。
  • 對於資源,使用<table>標籤和兩列表示,一列表示所有的屬性名,一列表示屬性對應的值。

Content-Types

根據上文內容,我主張使用的通用內容型別是“application/x-resource+format”和“application/x-collection+format”。在我看來,它們代表了RESTful API中常見的兩個極端情形的中間情形:

一類RESTful API只使用“空的”(譯者注:bare)XML、JSON或者YAML內容型別。這種情況下,內容型別只表示實體的型別是XML、JSON或者YAML。在我看來,這依然不夠。因為資源和集合會有一些特定的語義,例如“href”屬性,“link”屬性和type。Therefore, these are a specialization of XML, JSON and YAML and should be defined as such(譯者注:這裡不翻譯是因為沒看懂)。

另一類RESTful API會為資源模型中的每個資源型別都定義內容型別。一個例子是vSphere Director API。在我看來這也不妥。指定詳細的內容型別會導致API方和客戶端方認為這些型別有特定的介面。我認為所有的資源應該共享那些相同的、基本的介面,這些基本介面是符合RESTful設計原則,內容型別表示為“application/x-resource”。

一個原因是,通過 有利於內容型別 細節定義 的方法,內容型別可以和某些型別定義語言(如XMLSchema)中的屬性相關連。據推測,這有利於客戶端自動發現,因為客戶端知道某種型別的可用屬性。我在Forms討論(go into)了很多這個主題的細節,但總結下來我並不統一這個論點。

選擇表式格式

客戶端可以通過HTTP“Accept”頭表示客戶端使用哪種合適。HTTP RFC聲明瞭詳盡的規則,規則中可以請求多種格式,沒中格式都有自己的優先順序。下面的例子中客戶端告訴API它只接受YAML格式:

GET /api/collection
Accept: application/x-collection+yaml

譯者說

本文在HTTP協議的背景下,介紹了RESTful中的資源包含那些型別的資料;資源與JSON、XML、YAML等格式間的對映規則。作者支援將資源對映稱JSON格式。
閱讀本文還需要了解HTTP協議,否則很多屬於很難理解。

WeChat