2011-08-29 29 views
13

我試圖用Spring MVC構建一個RESTful API。我正在拍攝乾淨且易於管理的代碼,其中包結構遵循url結構。如何在Spring 3 MVC REST API中繼承RequestMappings

所以這是我有:

// com.test.api.library 
@RequestMapping("/library/{libraryId}") 
public Library getLibrary(@PathVariable long libraryId) { 
    return service.getLibraryById(libraryId); 
} 

// com.test.api.library.book 
@RequestMapping("/library/{libraryId}/book/{bookId}") 
public Book getBook(@PathVariable long libraryId, @PathVariable long bookId) { 
    Library library service.getLibraryById(libraryId); 
    return library.getBookById(bookId); 
} 

雖然這工作,我覺得很雜亂,而且容易出錯不得不重複「/庫/ {}庫Id 「在所有繼承的@RequestMappings中,/ library很可能是API的一大部分的根,它應該被寫入一次並且被重用,而不是隨處可用。

我想這本書級改寫爲這樣的:

// com.test.api.library.book 
@RequestMapping("/book/{bookId}") 
public Book getBook(@PathVariable long bookId) { 
    // long libraryId magically given to me from the library-class's getLibrary() 

    Library library service.getLibraryById(libraryId); 
    return library.getBookById(bookId); 
} 

有什麼辦法Spring可以幫助我在這裏?我可以使用普通的java繼承,spring註解或其他任何能夠幫助我不寫「/ library/{libraryId}」作爲我寫過的每個url的一部分的東西。

回答

1

我不認爲這是可能的。但是您可以在課程本身上添加@RequestMapping註釋,這樣至少可以爲您節省一些打字的時間。

+0

是的,我意識到這種可能性,它比僅僅註釋這些方法更好,但距離完美還有很長的路要走。 –

+0

根據你的回答,從完美的不清晰到我想要的東西還有很長的路要走。你能否展示一對夫婦資源的完整url,並描述你想讓他們處理哪些控制器? – SingleShot

+0

對,這可能是一個奇怪的例子,但... '/ country/{countryCode}/state/{stateCode}/city/{cityCode}/street/{streetCode}/number/{streetNumber}' 這些步驟(即國家,州,城市,街道和號碼)應該進入它自己的控制器,並且每個步驟都有一個動詞子集。主要問題是,當我執行numberController時,我不會從國家一路重複url,並且我不想「知道」countryCode是此資源的關鍵。我只是想「擁有」它。 –

-1
@Controller 
@RequestMapping("/library/{libraryId}") 
public class HelloWorldController { 

    @RequestMapping(value="/book/{bookId}") 
    public ModelAndView helloWorld() { 
     .... 
    } 

} 
+0

這不能回答我的問題。我很清楚這種可能性(如果沒有其他人指出Bozho給出的答案)。這樣做意味着我必須將所有的子資源放到同一個控制器的庫中。這是可能的,但不是我要求的。 無論如何。 –

4

我相信這個問題已經被問&纔回答:Spring MVC @RequestMapping Inheritance

這就是說,這裏是減少重複信息的數量的一種方式。我實際上並沒有在自己的代碼中這樣做,因爲我認爲將代碼旁邊的URI更容易維護,即使它意味着一點點重複。

@RequestMapping(URI_LIBRARY) 
public interface LibraryNamespace { 
    public static String URI_LIBRARY = "/library/{libraryId}"; 
} 

@RequestMapping(URI_BOOK) 
public interface BookNamespace { 
    public static String URI_BOOK = LibraryNamespace.URI_LIBRARY + "/book/{bookId}"; 
} 

@Controller 
public class LibraryController implements LibraryNamespace { 
    @RequestMapping("") 
    public Library get(@PathVariable long libraryId) { 
    return service.getLibraryById(libraryId); 
    } 
} 

@Controller 
public class BookController implements BookNamespace { 
    @RequestMapping("") 
    public Book get(@PathVariable long libraryId, @PathVariable long bookId) { 
    Library library service.getLibraryById(libraryId); 
    return library.getBookById(bookId); 
    } 
} 

由於我自己不會採取這種方法,我實際上沒有嘗試過這種解決方案!根據我對Spring的理解,我認爲它應該可以通過...

+0

我開始認爲答案是「不,這是不可能的」。你的想法增加了抽象層次,但仍然不能解決處理「@PathVariable long libraryId」更接近庫類的地方的主要問題。 來自「正常」的Java我習慣於繼承,並讓父類處理它自己的變量,只是讓子類處理特定於他們的東西。 無論如何。 –

+0

這是不可能的。對不起,我沒有在這裏說得更清楚。我認爲我所聯繫的問題/答案是非常清楚的。 – jtoberon

+0

我已經這樣做了。如果將此方法與多態父方法結合使用,則可以獲得DRY路徑和分離關注參數。我會挖掘我的代碼併發佈一個答案。 – Alex

3

使用多態父親方法。

@Controller 
public class CommentsController { 
    @RequestMapping(value="/comments", method = RequestMethod.GET) 
    public @ResponseBody String index() { 
     /* kludge to allow optional path parameters */ 
     return index(null, null); 
    } 

    @RequestMapping(value="/{parent_collection}/{parent_id}/comments", method = RequestMethod.GET) 
    public @ResponseBody String index(@PathVariable("parent_collection") String parentCollection, @PathVariable("parent_id") String parentId) { 
     if (parentCollection == null) { 
      return "all comments"; 
     } 
     else if ((parentCollection != null) && (parentCollection.equals("posts"))) { 
      /* get parent, then get comments for parent */ 
      return "comments for single post"; 
     } 
     else if ((parentCollection != null) && (parentCollection.equals("customers"))) { 
      /* get parent, then get comments for parent */ 
      return "comments for single customer"; 
     } 
     else if ((parentCollection != null) && (parentCollection.equals("movies"))) { 
      /* get parent, then get comments for parent */ 
      return "comments for single movie"; 
     } 
    } 

    @RequestMapping(value = "/comments/{id}", method = RequestMethod.GET) 
    public @ResponseBody String show(@PathVariable Integer id) { 
     /* kludge to allow optional path parameters */ 
     return show(null, null, id); 
    } 

    @RequestMapping(value = "/{parent_collection}/{parent_id}/comments/{id}", method = RequestMethod.GET) 
    public @ResponseBody String show(@PathVariable("parent_collection") String parentCollection, @PathVariable("parent_id") String parentId, @PathVariable Integer id) { 
     /* get comment, then get parent from foreign key */ 

     if (parentCollection == null) { 
      return "single comment"; 
     } 
     else if ((parentCollection != null) && (parentCollection.equals("posts"))) { 
      return "single comment for single post"; 
     } 
     else if ((parentCollection != null) && (parentCollection.equals("customers"))) { 
      return "single comment for single customer"; 
     } 
     else if ((parentCollection != null) && (parentCollection.equals("movies"))) { 
      return "single comment for single movie"; 
     } 
    } 
} 

此外,你可以使用一個基本的控制器來路由URI前綴父資源(/libraries/{library_id}/../..),父模型添加到請求範圍,然後讓普通請求映射處理URI的其餘部分兒童資源(/../../books/1)。我手邊沒有這樣的例子。

附註。單一的嵌套資源通常被認爲是URI設計的反模式。控制器應該處理自己的資源。最常見的實現方式使單一嵌套資源的唯一關鍵,即不依賴於其父資源。例如,一個數據庫記錄主鍵。但是,在某些情況下,密鑰可能不是唯一的,例如序號或位置值(例如,第1冊,第1章,第2章),或者甚至可能是自然密鑰(例如書籍ISBN,人員SSN,電子郵件地址,用戶名,文件名)。

嵌套資源規範URI的示例:

  • /articles => ArticlesController#索引
  • /articles/1 => ArticlesController#表明
  • /articles/1/comments => CommentsController#索引
  • /articles/1/comments/2 => CommentsController#顯示(好吧,但不是首選)
  • /comments/2 => CommentsController#show(preferred )