RESTful API設計系列三:資源
說明
資源是任何RESTful API中的基本概念。資源是物件,包括型別、關聯的資料、資源間的關係以及資源上的操作集合。它和麵向物件程式語言中的物件類似,不同點在於資源
只定義了有限的標準方法(對應HTTP協議中標準的GET,POST,PUT,DELETE方法),而物件例項可以有很多方法。
資源可以被分類到不同的集合中。每個集合都包含一種型別的資源,因此集合都是均勻(homogeneous
)的。資源也可以不分到集合裡,這些資源我們稱為singleton resources
。
集合可以全域性存在,即API頂層(譯者注:原文at the top level of an API),也可以包含在single resource中。後文中,我們把這種集合稱為sub-collections
下圖描述了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資料模型對映的資源資料:
- 資源被建模為JSON物件。資源的型別儲存在特殊的鍵值對
_type
中。 - 資源中的資料表示為JSON物件中的鍵值對。為了避免和JSON物件內部鍵值對衝突,鍵不能以“_”開頭。
- 鍵值對中的值可以是JSON中任意原生型別: string、number、boolean、null或者arrays。值還可以是物件,這種情況下值表示巢狀的資源。
- 集合表示成物件陣列。
我們也會把鍵值對認為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協議,否則很多屬於很難理解。