Как наследовать RequestMappings в Spring 3 MVC REST API - PullRequest
14 голосов
/ 29 августа 2011

Я пытаюсь создать RESTful API с помощью Spring MVC.Я снимаю чистый и управляемый код, где структура пакета соответствует структуре URL.

Итак, вот что у меня получилось:

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

Хотя это работает, я нахожу грязным и подверженным ошибкам повторение «/ library / {libraryId}» во всех унаследованных @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, который я когда-либо пишу.

Ответы [ 4 ]

4 голосов
/ 10 сентября 2011

Я полагаю, что на этот вопрос задавали и отвечали раньше: 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, я думаю, что это должно работать, хотя ...

3 голосов
/ 07 марта 2013

Используйте полиморфный родительский подход.

@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 # index
  • /articles/1 => ArticlesController # show
  • /articles/1/comments => CommentsController # index
  • /articles/1/comments/2 => CommentsController # show (хорошо, но не предпочтительно)
  • /comments/2 => CommentsController # show (предпочтительно)
1 голос
/ 29 августа 2011

Я не думаю, что это возможно. Но вы можете иметь аннотацию @RequestMapping для самого класса, так что это сэкономит вам хотя бы некоторую печать.

0 голосов
/ 09 сентября 2011
@Controller
@RequestMapping("/library/{libraryId}")
public class HelloWorldController {

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

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...