如何在Spring 3 MVC REST API中继承RequestMappings

时间:2011-08-29 10:13:46

标签: java spring spring-mvc

我正在尝试使用Spring MVC构建RESTful API。我正在拍摄干净且易于管理的代码,其中包结构遵循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);
}

虽然这很有效,但我觉得它在所有继承的@RequestMappings中都要重复“/ library / {libraryId}”而且很容易出错,而且库很可能是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 个答案:

答案 0 :(得分: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的理解,我认为它应该可以工作......

答案 1 :(得分: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 #show(好的,但不是首选)
  • /comments/2 => CommentsController #show(首选)

答案 2 :(得分:1)

我不认为这是可能的。但是您可以在类本身上使用@RequestMapping注释,这样可以为您节省至少一些输入。

答案 3 :(得分:-1)

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

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

}