2011-08-29 8 views
13

Próbuję zbudować RESTful API przy użyciu Spring MVC. Stawiam na czysty i łatwy w zarządzaniu kod, w którym struktura pakietu podąża za strukturą adresu URL.Jak dziedziczyć RequestMappings w Spring 3 MVC REST API

Więc tutaj jest to, co mam:

// 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); 
} 

Chociaż to działa, uważam, że to brudny i podatne na błędy musiał powtórzyć „/ Library/{} libraryId "we wszystkich dziedziczonych plikach @RequestMappings,/library prawdopodobnie będzie źródłem dużej części interfejsu API i powinno być napisane raz i ponownie użyte zamiast wszędzie napisane.

Chciałbym przepisać książkę klasy do czegoś takiego:

// 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); 
} 

Czy istnieje jakiś sposób, wiosna może mi tu pomóc? Dopuszczalne jest używanie normalnego dziedziczenia java, adnotacji wiosennej lub czegokolwiek innego, co pomaga mi nie pisać "/ library/{libraryId}" jako części każdego adresu URL, jaki kiedykolwiek napisałem.

Odpowiedz

1

Nie sądzę, że to możliwe. Ale możesz mieć adnotację @RequestMapping na samej klasie, dzięki czemu zaoszczędzisz przynajmniej trochę pisania.

+0

Tak, jestem świadomy tej możliwości i jest to o wiele lepsze niż tylko opisywanie metod, ale wciąż daleko do ideału. –

+0

Na podstawie Twojej odpowiedzi, że jest to dalekie od doskonałego, niejasne dla mnie, czego chcesz. Czy możesz pokazać pełne adresy URL kilku zasobów i opisać, które kontrolery chcesz obsługiwać? – SingleShot

+0

Dobrze, może to być dziwny przykład, ale ... '/ country/{countryCode}/state/{stateCode}/city/{cityCode}/street/{streetCode}/number/{streetNumber}' każdy z te kroki (tj. kraj, stan, miasto, ulica i numer) powinny przejść do jego własnego kontrolera, a każda z nich ma podzbiór czasowników na nich. Głównym pytaniem jest, kiedy robię numberController, że nie będę powtarzać adresu URL z kraju i nie będę "wiedzieć", że kod kraju jest kluczem do tego zasobu. Po prostu chcę "mieć" to. –

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

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

} 
+0

To nie odpowiada na moje pytanie. Doskonale zdaję sobie sprawę z tej możliwości (jeśli nic innego nie wskazano w odpowiedzi udzielonej przez Bozho). W ten sposób muszę umieścić wszystkie poddziedziny w bibliotece w tym samym kontrolerze. Jest to możliwe, ale nie o to proszę. Dzięki i tak. –

4

Wierzę, że to pytanie zostało zadane & odpowiedział przed: Spring MVC @RequestMapping Inheritance

powiedział, że tu jest jeden sposób, aby zmniejszyć ilość powielonych informacji. Tak naprawdę nie robię tego w moim własnym kodzie, ponieważ myślę, że posiadanie URI tuż obok kodu jest łatwiejsze w utrzymaniu, nawet jeśli oznacza to małe duplikowanie.

@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); 
    } 
} 

Ponieważ sam nie podjąłbym takiego podejścia, nie próbowałem tego rozwiązania! Opierając się na mojej wiedzy na temat Spring, myślę, że powinno działać ...

+0

Zaczynam myśleć, że odpowiedź brzmi "nie, to niemożliwe". Twój pomysł dodaje poziom abstrakcji, ale nadal nie rozwiązuje głównego problemu, który zajmuje się "@PathVariable long libraryId" gdzieś bliżej klasy biblioteki. Pochodzenie z "normalnego" java Jestem przyzwyczajony do dziedziczenia i pozwalam klasie macierzystej obsługiwać własne zmienne i po prostu pozwolić, aby podklasy traktowały to, co jest dla nich specyficzne. Dzięki i tak. –

+0

To niemożliwe. Przepraszam, że nie wyjaśniłem tego tutaj. Myślałem, że pytanie/odpowiedź, z którą się łączyłem, było wystarczająco jasne. – jtoberon

+0

Zrobiłem to. Jeśli połączysz to podejście z polimorficznym podejściem rodzicielskim, możesz uzyskać ścieżki DRY i argumenty separacji-dotyczy. Odkopię mój kod i opublikuję odpowiedź. – Alex

3

Użyj podejścia polimorficznego rodzica.

@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"; 
     } 
    } 
} 

Dodatkowo można użyć podstawowy kontroler do trasy przedrostek URI do zasobów dominującej (/libraries/{library_id}/../..), dodać modeli nadrzędnych do zakresu żądania, a następnie niech regularne mapowania żądania obsłużyć resztę URI zasoby dziecięce (/../../books/1). Nie mam tego przykładu z ręki.

Notatka boczna. Pojedyncze zasoby zagnieżdżone są ogólnie uważane za antypodstawy projektowania URI. Kontroler powinien obsługiwać własne zasoby. Najczęściej stosowane implementacje sprawiają, że klucz unikalnego pojedynczego zasobu zagnieżdżonego jest unikalny, tj. Nie jest zależny od jego zasobów nadrzędnych. Na przykład klucz podstawowy rekordu bazy danych. Istnieją jednak sytuacje, w których klucz może nie być unikalny, taki jak wartość porządkowa lub pozycja (np. Książka 1, rozdział 1, rozdział 2), a może nawet naturalny klucz (np. Książka ISBN, osoba SSN, adres e-mail , nazwa użytkownika, nazwa pliku).

Przykład kanonicznych URI na zagnieżdżone środków:

  • /articles => ArticlesController Wskaźnik
  • /articles/1 => ArticlesController # pokazuje
  • /articles/1/comments => CommentsController Wskaźnik
  • /articles/1/comments/2 => CommentsController # pokaż (w porządku, ale nie jest zalecane)
  • /comments/2 => KomentarzeController # show (preferowane)
Powiązane problemy