Use a polymorphic parent approach.
@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";
}
}
}
Additionally, you could use a base controller to route the URI prefix to parent resources (/libraries/{library_id}/../..
), add the parent models to the request scope, and then let the regular request mappings handle the rest of the URI to child resources (/../../books/1
). I don't have an example of this off-hand.
Side note. Singular nested resources are generally regarded as an antipattern for URI design. A controller should handle its own resources. The most common implementations make the key for the singular nested resource unique, i.e., not dependent on its parent resource. For instance, a database record primary key. However, there are situations where the key might not be unique, such as an ordinal or position value (e.g., book 1, chapter 1, chapter 2), or maybe even a natural key (e.g., book ISBN, person SSN, email address, username, filename).
Example of canonical URIs for nested resources:
/articles
=> ArticlesController#index
/articles/1
=> ArticlesController#show
/articles/1/comments
=> CommentsController#index
/articles/1/comments/2
=> CommentsController#show (okay, but not preferred)
/comments/2
=> CommentsController#show (preferred)