Java 如何在Spring3MVCRestAPI中继承RequestMappings
我正在尝试使用SpringMVC构建一个RESTfulAPI。我正在寻找干净且易于管理的代码,其中包结构遵循url结构 这就是我得到的:Java 如何在Spring3MVCRestAPI中继承RequestMappings,java,spring,spring-mvc,Java,Spring,Spring Mvc,我正在尝试使用SpringMVC构建一个RESTfulAPI。我正在寻找干净且易于管理的代码,其中包结构遵循url结构 这就是我得到的: // com.test.api.library @RequestMapping("/library/{libraryId}") public Library getLibrary(@PathVariable long libraryId) { return service.getLibraryById(libraryId); } 虽然这样做很有效,但
// com.test.api.library
@RequestMapping("/library/{libraryId}")
public Library getLibrary(@PathVariable long libraryId) {
return service.getLibraryById(libraryId);
}
虽然这样做很有效,但我发现它很混乱并且容易出错,在所有继承的@RequestMappings中都必须重复“/library/{libraryId}”,而/library很可能是API很大一部分的根目录,应该编写一次并重用,而不是到处编写
我想将book类改写为以下内容:
// 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);
}
春天有什么办法可以帮我吗?我可以接受使用普通的java继承、spring注释或任何其他帮助我不将“/library/{libraryId}”作为我编写的每个url的一部分。我认为这是不可能的。但是您可以在类本身上添加
@RequestMapping
注释,这样至少可以节省一些输入。我相信这个问题以前已经被问过并回答过了:
@Controller
@RequestMapping("/library/{libraryId}")
public class HelloWorldController {
@RequestMapping(value="/book/{bookId}")
public ModelAndView helloWorld() {
....
}
}
也就是说,这里有一种减少重复信息量的方法。实际上,我在自己的代码中并没有这样做,因为我认为将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的理解,我认为它应该是有效的,尽管…使用多态父方法
@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\u id}/./..
),将父模型添加到请求范围,然后让常规请求映射将URI的其余部分处理到子资源(/../books/1
)。我手头没有这方面的例子
旁注。单一嵌套资源通常被视为URI设计的反模式。控制器应该处理自己的资源。最常见的实现使单个嵌套资源的键唯一,即不依赖于其父资源。例如,数据库记录主键。但是,在某些情况下,密钥可能不是唯一的,例如序号或位置值(例如,book 1,chapter 1,chapter 2),甚至可能是自然密钥(例如,book ISBN,person SSN,email address,username,filename)
嵌套资源的规范URI示例:
=>ArticlesController#索引/articles
=>ArticlesController#show/articles/1
=>CommentsController#索引/articles/1/comments
=>CommentsController#show(可以,但不是首选)/articles/1/comments/2
=>CommentsController#show(首选)/comments/2
/country/{countryCode}/state/{stateCode}/city/{cityCode}/street/{streetCode}/number/{streetNumber}
这些步骤中的每一步(即国家、州、城市、街道和号码)都应该进入它自己的控制器,并且每个步骤上都有一个子动词。主要的问题是,当我使用numberController时,我不想从国家一路重复url,我也不想“知道”国家代码是这个资源的关键。我只是想“拥有”它。这有点偏离目标,但我通过将命令/动词对(最终与实体/id对相同——命令是“查找”)映射到存储在数据库中的bean(由Groovy实现)来实现类似的系统。每个结果类型都有一个可用命令列表(因此一个国家有可用的州)。由于我的用例,它变得相当复杂,但在运行时是灵活的、可扩展的。也许它会给你们一些想法,尽管我知道它不能满足你们的迫切需要。我会悬赏给你们这个答案。我不想把赏金白白浪费在任何事情上,这在某种程度上是一个“正确”的答案,但这并不能回答我的问题。我很清楚这种可能性(如果没有别的,那就是波佐在回答中指出的)。这样做意味着我必须将库中的所有子资源放在同一个控制器中。这当然是可能的,但不是我想要的。无论如何,谢谢。我开始认为答案是“不,不可能”。您的想法增加了一个抽象级别,但仍然没有解决主要问题,即在更接近库类的地方处理“@PathVariable long libraryId”。来自“普通”java,我习惯于继承,让父类处理自己的变量,只让子类处理特定于它们的变量。无论如何,谢谢。这是不可能的。对不起,我没有在这里说得更清楚。我认为我链接的问题/答案足够清楚。我已经做到了。如果将此方法与多态父方法相结合,则可以获得干路径和关注点分离参数。我会找出我的密码并给出答案。我同意你的看法。将URI放在代码旁边更易于维护和可读。
@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";
}
}
}