2014-01-11 168 views
4

我正在開發REST服務,並且我試圖遵守Roy Fielding醫生的約定和指導原則。REST資源路徑設計

我將我的服務描述爲暴露一組資源的端點。資源由URI標識,並且api客戶端可以使用HTTP語義操作資源(即,不同的HTTP動詞映射到通過URI的相應操作)。

準則指出這些URI應該以分層方式定義,反映對象層次結構。這對於創建資源非常有用,因爲在後端我們需要數據來執行創建操作。但是,在進一步的操作中,URI中包含的許多信息甚至不會被服務使用,因爲通常資源Id本身足以唯一地標識操作目標。

示例:考慮一個暴露產品創建和管理的Api。還要考慮到產品與品牌相關聯。上創建它是有道理的,執行以下動作: HTTP POST /品牌/ {brand_id} /產品

創建返回與創建一個HTTP 201 [含有必要輸入到創作產品的機構]位置標題,顯示新創建的產品的位置。

在進一步的操作,客戶端可以通過做訪問產品: HTTP PUT /品牌/ {brand_id} /產品/ {PRODUCT_ID} HTTP DELETE /品牌/ {brand_id} /產品/ {PRODUCT_ID} 等

但是,由於產品ID在產品範圍內是通用的,因此可以按如下方式執行以下操作: /Product/{product_id} 爲保持一致性原因,我只保留/ Brand/{brand_id}前綴。事實上,品牌ID正被服務忽略。你是否認爲這是一種良好的做法,並且爲了保持清晰明確的ServiceInterface定義而合理?這樣做的好處是什麼,這是一種什麼樣的方式?

此外,任何關於URI定義最佳實踐的指針將不勝感激。

在此先感謝

回答

8

你是在說:

準則指出這些URI應以分層的方式來定義,反映了對象層次。

儘管通常以這種方式完成,但它對於RESTful API設計來說並不相關。 Roy Fielding有一個nice article解決了關於REST的常見誤解。他甚至說:

REST API不能定義固定的資源名稱或層次結構(客戶端和服務器的明顯耦合)。

一個REST API應該超出了最初的URI沒有先驗知識輸入...

所以,不要在您的網址應的資源內傳遞信息編碼。即使用人工和非感知URI替換所有URL,RESTful API也應該可以工作。 (作爲任何人,我都喜歡可理解的URI,但是作爲一種心理練習來檢查你的「RESTfullness」,它是相當不錯的)。

爲對象「層次結構」建模URI的問題是層次結構並不是非常明顯看起來好像。 (教師,課程和學生之間的對象層次結構是什麼?)。物體通常處於一個關係網絡中,並且不屬於另一個物體之下。一個產品可能屬於一個品牌,但您可能有多個供應商(涵蓋多個品牌的產品子集)。 REST非常適合表達複雜的關係網。整個互聯網/網絡以這種方式工作。

代替在層次結構中編碼關係,只需在資源中定義指向相關對象的超鏈接即可。

對於您的具體示例,我將使用POST/product /創建新產品,並在創建產品時在資源表示形式中指向您的/ brand/xzy的鏈接。

如果您想知道爲特定品牌定義了哪些產品,只需在返回的GET/brand/xzy表示中包含鏈接列表。如果您想擁有代表此關係的顯式資源,您仍然可以將GET/brand/{id}/products定義爲URL(或/ brandproducts/xzy或/ 34143453453),並將其作爲品牌資源中的鏈接返回。

不要過多考慮你的URI的設計,更多地考慮你在資源中提供的信息。確保它提供了您的客戶端在從您的API接收它後可能想查看或處理的所有資源表示的鏈接。

1

我覺得這是關鍵的評論:

產品與品牌相關聯。

相關字告訴我你需要鏈接資源集中在一起。所以,假設品牌和產品之間存在關聯。每個資源都會按照您所描述的方式擁有自己的一套方法(GET,PUT等),但是這些表示應該具有描述其關聯的其他資源的鏈接。鏈接取決於關聯類型(一對一,一對多,多對一,多對多)和方向。

例如,假設有這個產品api.example.com的規範要求:

GET /product/12345 

它返回產品的一定的代表性。爲了簡單起見,我將使用XML來表示,但它可以是XHTML,JSON或任何您需要的。因此,產品12345的簡單表示:

HTTP/1.1 200 OK 
Content-Type: application/xml; charset=utf-8 
Content-Length: ... 

<?xml version="1.0"?> 
<Product href="http://api.example.com/product/12345" rel="self" type="application/xml"/> 
    <Name>Macaroni and Cheese</Name> 
    <Brand href="http://api.example.com/brand/7329" type="application/xml"/> 
    <Manufacturer href="http://api.example.com/manufacturer/kraft" rel="parent" type="application/xml"/> 
</Product> 

正如您所看到的,我將鏈接嵌入到描述每個關係的產品12345的表示中。只要有可能,我儘量遵守HATEOAS限制:

  • 在當前資源和相關資源之間有顯式鏈接。
  • 可選的關係描述(「rel」)。 「自我」和「父母」是關於當前資源和鏈接引用的資源的關係的描述。
  • 應該請求的可選的首選MIME類型。這描述了客戶在發出後續請求時應該期望的文檔類型。
  • 不透明URL而不是原始標識符。客戶可以「導航」到URL,而無需使用一些約定來構建它們。請注意,網址並不總是需要包含唯一的數據庫標識符或密鑰(如「/ brands/kraft」)。

爲了擴展一些先進的概念,讓我們假設產品有其他關係。也許產品有層次關係或產品取代其他產品。所有這些複雜的關係都可以用鏈接來表示。所以,產品12345的先進表示:

HTTP/1.1 200 OK 
Content-Type: application/xml; charset=utf-8 
Content-Length: ... 

<?xml version="1.0"?> 
<Product href="http://api.example.com/product/12345" rel="self" type="application/xml"/> 
    <Name>Macaroni and Cheese</Name> 
    <Brand href="http://api.example.com/brand/7329" rel="parent" type="application/xml"/> 
    <Manufacturer href="http://api.example.com/manufacturer/kraft" type="application/xml"/> 
    <!-- Other product data --> 
    <Related> 
    <Product href="http://api.example.com/product/29180" rel="prev" type="application/xml"/> 
    <Product href="http://api.example.com/product/39201" rel="next" type="application/xml"/> 
    </Related>  
</Product> 

在這個例子中,我使用「上一頁」和「下」來表示的產品鏈。 「prev」可以被解釋爲「被取代」並且「下一個」被解釋爲「被取代」。您可以使用「替代」和「替代」作爲rel值,但通常使用「prev」和「next」。這真的取決於你。

+0

看起來你的建議是:保持你的資源路徑簡單,並在每個資源成員中包含鏈接。 你只處理Get情況。你會如何創建一個新產品?也就是說,你會在創作時指出哪些製造商與新產品有關?將其包含在請求正文中?這聽起來不對。聽起來像這樣的信息將被包含在資源URI中 –

+0

鏈接將被包括在請求主體中,因爲那是該資源的表示。您必須對請求主體執行適當的檢查,以確保製造商和品牌確實存在。 –