2010-06-08 125 views
5

這是關於服務架構策略的更多問題,我們正在基於後端的休息服務構建大型Web系統。目前我們正在嘗試制定一些內部標準,以開發其他服務。RESTful服務架構問題

某些查詢返回的實體的列表,例如讓我們考慮我們的圖片庫中檢索服務:/ gell_all_galeries,返回下一個響應:

<galleries> 
    <gallery> 
     <id>some_gallery_id</id> 
     <name>my photos</name> 
     <photos> 
      <photo> 
       <id>123</id> 
       <name>my photo</name> 
       <location>http://mysite/photo/show/123</location> 
       ...... 
       <author> 
        <id>some_id</id> 
        <name>some name</name> 
        ....... 
       <author> 
      </photo> 
      <photo> ..... </photo> 
      <photo> ..... </photo> 
      <photo> ..... </photo> 
      <photo> ..... </photo> 
    </photos> 
    </gallery> 
    <gallery> .... </gallery> 
    <gallery> .... </gallery> 
    <gallery> .... </gallery> 
    <gallery> .... </gallery> 
</galleries> 

正如你在這裏看到,響應相當大又重,而且並不總是我們需要這樣的深層信息。通常的解決方法是使用或每家畫廊http://ru.wikipedia.org/wiki/Atom元素全庫數據,而不是:

<galleries> 
    <gallery> 
     <id>some_gallery_id</id> 
     <link href="http://mysite/gallery/some_gallery_id"/> 
    </gallery> 
    <gallery> 
     <id>second_gallery_id</id> 
     <link href="http://mysite/gallery/second_gallery_id"/> 
    </gallery> 
    <gallery> .... </gallery> 
    <gallery> .... </gallery> 
    <gallery> .... </gallery> 
    <gallery> .... </gallery> 
</galleries> 

的第一個問題,是未來:也許相反,我們甚至不應該使用和類型,以及只使用通用的所有資源該返回列表對象:

<list> 
    <item><link href="http://mysite/gallery/some_gallery_id"/></item> 
    <item><link href="http://mysite/gallery/other_gallery_id"/></item> 
    <item>....</item> 
</list> 

而第二個問題,用戶以後嘗試檢索有關一些具體畫廊的信息,他將使用例如http://mysite/gallery/some_gallery_id鏈接,他應該怎麼看到的結果嗎?

它應該是:

<gallery> 
     <id>some_gallery_id</id> 
     <name>my photos</name> 
     <photos> 
      <photo> 
       <id>123</id> 
       <name>my photo</name> 
       <location>http://mysite/photo/show/123</location> 
       ...... 
       <author> 
        <id>some_id</id> 
        <name>some name</name> 
        ....... 
       <author> 
      </photo> 
      <photo> ..... </photo> 
      <photo> ..... </photo> 
      <photo> ..... </photo> 
      <photo> ..... </photo> 
    </photos> 
    </gallery> 

或:

<gallery> 
     <id>some_gallery_id</id> 
     <name>my photos</name> 
     <photos> 
      <photo><link href="http://mysite/photo/11111"/></photo> 
      <photo><link href="http://mysite/photo/22222"/></photo> 
      <photo><link href="http://mysite/photo/33333"/> </photo> 
      <photo> ..... </photo> 
    </photos> 
    </gallery> 

<gallery> 
     <id>some_gallery_id</id> 
     <name>my photos</name> 
     <photos> 
      <photo> 
       <link href="http://mysite/photo/11111"/> 
       <author> 
        <link href="http://mysite/author/11111"/> 
       </author> 
      </photo> 
      <photo> 
       <link href="http://mysite/photo/22222"/> 
       <author> 
        <link href="http://mysite/author/11111"/> 
       </author> 
      </photo> 
      <photo> 
       <link href="http://mysite/photo/33333"/> 
       <author> 
        <link href="http://mysite/author/11111"/> 
       </author> 
      </photo> 
      <photo> ..... </photo> 
    </photos> 
    </gallery> 

我的意思是,如果我們使用的鏈接,而不是完整的對象的信息,有多深,我們應該去那裏?我是否應該在照片中顯示作者等等。

大概是我的問題含糊不清,但我想要做的是在這種情況下爲所有團隊成員在未來遵循一般策略。

回答

0

您可以隨時使用屬性。

<gallery id = "1" name = "Gallery 1"> 
     <photos> 
      <photo id="1" link="http://mysite/photo/11111" /> 
      <photo id="2" link="http://mysite/photo/22222" /> 
      <photo id="3" link="http://mysite/photo/33333" /> 
     </photos> 
    </gallery> 

或者你可以使用JSON我更喜歡它,因爲它比XML更輕鬆,更輕。

{ 
    "gallery": { 
     "id": "1", 
     "name": "Gallery 1", 
     "photos": [ 
      { 
       "id": "1", 
       "link": "http://mysite/photo/11111" 
      }, 
      { 
       "photo": "2", 
       "link": "http://mysite/photo/22222" 
      }, 
      { 
       "photo": "3", 
       "link": "http://mysite/photo/33333" 
      } 
     ] 
    } 
+0

我使用Java和Apache CXF來提供RESTful服務。這具有能夠爲相同資源提供* XML和JSON兩者的優點,這取決於客戶說它喜歡什麼(即通過內容協商)。 – 2010-06-08 12:46:50

2

需要考慮的一件好事是你打算讓客戶端檢索數據。如果您打算讓客戶端抓取大量關於許多照片的信息,那麼只有<photo href="..."/>的列表可能不是最佳的,因爲客戶端然後將被迫對他們需要的每個照片資源執行GET請求。

我可以想到一些有趣的方法繞過我的頭頂。

在查詢列表時,您可以允許客戶端指定他們想要檢索的字段作爲查詢參數,例如,:

GET http://www.example.com/photos?_fields=author,fileSize 

這可能再返回類似:

<photos href="/photos?_fields=author,fileSize"> 
    <photo href="/photos/15"> 
     <author href="/authors/2245"/> 
     <fileSize>32MB</fileSize> 
    </photo> 
    ... 
</photos> 

或者,你可以把它簡單通過允許客戶端指定某種最大的「深度」財產;這有點粗糙,但可以有效地使用。例如,如果客戶端指定深度爲2,則您將返回<gallery>下的所有內容以及每個<photo>的所有子元素。

GET /galleries?depth=2 

可能返回類似:

<galleries> 
    <id>22</id> 
    <name>My Gallery</name> 
    <!-- full gallery data --> 
    <photos href="/photos?gallery=/galleries/22"> 
    <photo href="/photos/99"> 
     <id>99</id> 
     <author href="/authors/4381"/><!-- href instead of including nested author data --> 
     <fileSize>24MB</fileSize> 
     <!-- full photo data --> 
    </photo> 
    ... 
    </photos> 
</galleries> 

除了這一點,如果你擔心客戶端查詢一次很多很多的記錄(例如,如果有成千上萬的照片或畫廊),您可能需要考慮爲列表進行某種分頁。這可能包括設置硬盤最大代碼中的結果,併爲客戶提供了鏈接到下一首/上頁:

GET /photos?gallery=/galleries/59 

可能返回:

<photos href="/photos?gallery=/galleries/59&_max=100&_first=100" next="/photos?gallery=/galleries/59&_max=100&_first=200" prev="/photos?gallery=/galleries/59&_max=100&_first=0" count="100" total="3528"> 
    .... 
</photos> 

客戶端可以控制_first_max性質,但不能在特定的配置閾值上增加_max。您將返回標記中頁面的「找到」結果的數量以及可用結果的總數。這可以幫助你減少響應大小,你提到的可能是一個問題。這可以與上面列出的選項並行完成。

最終,這取決於您希望服務器如何指示客戶端檢索數據。如果你不希望他們爲每張照片做一個GET,那麼你可能想要爲他們提供更方便的方法來獲取更深的數據。但是如果你認爲你的服務器可以處理體面的負載,並且可以進行服務器端優化(緩存,使用304狀態等),那麼僅僅使用href來返回淺表就更簡單了。

+2

我知道讓客戶選擇他們希望接收哪些數據元素的想法是流行的,但是您必須考慮如何嚴重限制您緩存響應的能力。如果你只有五個屬性,那麼想象你可能擁有的不同變化的響應數量。你是否緩存了所有這些緩存,並且緩存了它們,緩存了所有這些緩存,並在數據庫服務器上增加了額外負載,或者是否緩存在可以從完整緩存副本中提取數據子集的智能中介中? – 2010-06-08 13:00:56

+0

@達雷爾 - 一個好點。這可能取決於服務器是否需要緩存結果。如果不是,那麼它是可行的;但如果是這樣,我可能會避免它。 – 2010-06-08 13:15:20

+0

@Rob我認爲你的想法指定字段名稱需要返回是有道理的,因爲我記得Linked In Api以這種方式工作。謝謝。 – abovesun 2010-06-08 13:36:06

3

對於「我應該如何設計我的媒體類型」確實沒有正確或錯誤的答案。但是,選擇現有和設計新媒體類型時,有幾條非常重要的指導原則。

RESTful系統通過謹慎使用緩存來實現可擴展性。設計資源將內容分解成具有類似數據波動性的塊。例如,在您的場景中,您有一個包含照片的畫廊列表。我的猜測是,你不會經常添加/刪除畫廊,但你會定期添加/刪除照片。因此,確保您可以獲得沒有照片信息的畫廊列表是有意義的。這將使緩存該響應變得容易。

優化響應的大小對於性能可能很重要,但緩存更爲重要。通過線路發送0字節總是更高效。

儘管照片列表可能會更經常變化,但您仍然可以有效地使用緩存。通過使用if-modified-since標頭或etags,您不會保存網絡往返,但可以通過不傳輸未更改的表示來節省大量帶寬。

這是極其難難以設計的資源,非常適合所有情況因此,我建議你不要嘗試。適合您的特定用例的設計資源。如果出現其他用例,則創建新資源來處理這些用例。

沒有什麼錯與創建:

/gallery/foo/quickview 
/gallery/foo/detailedview 
/gallery/foo/justlinks 

你想使用Web框架,使得它很容易和便宜,以創建新的資源。資源很少會與您的域實體進行一對一映射,因此請隨時創建儘可能多的資源。

我最近的評論是關於媒體類型的選擇。你應該真的考慮使用像Atom這樣的服務。 Atom非常適合管理事物清單,並且具有處理媒體元素(如照片)的所有機制。

當大多數人開始使用REST服務時,他們習慣於認爲他們可以提供直接的application/xml或application/json作爲媒體類型。在某些特殊情況下這是完全可行的,但是隨着您開始實施更多的REST約束,您會發現這些通用媒體類型格式將限制您在許多情況下可以實現的效益。目前,不要擔心太多,只要注意選擇application/xhtml,RDF或Atom等「真實」媒體類型總是比較安全的,如果您選擇application/xml,則可能遇到困難稍後的。

+0

感謝Darrel,我們從Atom模式取得了鏈接類型定義,而且我們沒有使用Entry或Feed類型。如果你感興趣,我們正在使用Java Jersey rest實現。 也許我會同意你的陳述,這裏沒有共同的設計規則,最終的設計決定取決於具體的使用案例。 – abovesun 2010-06-08 13:43:25

1

這真的取決於你的情況。你需要知道客戶如何使用這個知道如何設計你的Resource Proxies

我建議你不要迷路在「選擇十字路口」。根據您對客戶端使用情況的假設進行一次實施。看看整個事情是如何使用和行爲的,如果需要的話可以稍後調整。沖洗。重複。做它永久的Beta方式:)