Indivon
Indivon

Reputation: 1874

Spring REST reuse nested requstmapping

We have a REST API, for some comments. Currently, the most interesting URIs are:

GET /products/1/comments             // get all comments of product 1
GET /products/1/comments/5           // get the 5th comment of product 1
GET /products/1/comments/5/user      // get the user of the 5th comment
GET /products/1/comments/latest      // get the latest comment of product 1
GET /products/1/comments/latest/user // get the user of the latest comment

in addition, you can directly access comments

GET /comments/987                    // get the comment with id 987
GET /comments/987/user               // get the user of comment with id 987

so, we have two @RestController:

@RestController
@RequestMapping("/products/{productId}")
public class ProductsCommentsResource {

    @GetMapping(value = "/comments")
    public ResponseEntity<?> getComments(@PathVariable Long productId){
       // get all products...
    }

    @GetMapping(value = "/comments/{commentNr}")
    public ResponseEntity<?> getComment(@PathVariable Long productId, @PathVaraible Long commentNr){
       // get comment with number commentNr of product productId
    }

    @GetMapping(value = "/comments/{commentNr}/user")
    public ResponseEntity<?> getCommentUser(@PathVariable Long productId, @PathVaraible Long commentNr){
       // get the user of comment with commentNr of productId
    }

    @GetMapping(value = "/comments/latest")
    public ResponseEntity<?> getLatestComment(@PathVariable Long productId){
       // get latest commentNr and call getComment(productId, commentNr)
    }

    @GetMapping(value = "/comments/latest/user")
    public ResponseEntity<?> getLatestCommentUser(@PathVariable Long productId){
       // get latest commentNr and call getCommentUser(productId, commentNr)
    }
}

@RestController
@RequestMapping("/comments")
public class CommentsResource {
    @GetMapping(value = "/{commentId}")
    public ResponseEntity<?> getComment(@PathVaraible Long commentId){
       // get comment id commentId
    }

    @GetMapping(value = "/{commentId}/user")
    public ResponseEntity<?> getCommentUser(@PathVaraible Long commendId){
       // get the user of comment with id commentId
    }
}      

The latest is therefore only a replacement-keyword for "get the last commentNr and call the corresponding method with this commentId"

This is just an excerpt and in addition to the user, a comment has about 30 sub-and sub-sub-resources (including methods POST, DELETE etc.). Therefore, we have more or less everything three times.

So, obviously, we need to improve this, to remove duplicate code etc. The idea is to "encapsulate" the comments-resources and make it reusable by using the @RequestMapping annotated at the class.

We thought about a mechnism like:

Thus, we would need to have something that redirects - /products/1/comments/latest[what ever] to /comments/{commentId}[what ever] - /products/1/comments/5[what ever] also to /comments/{commentId}[what ever]

and /comments/{commentId} would be the only the implementation.

However, we did not find anything suitable in the spring docs...

Upvotes: 3

Views: 2371

Answers (1)

Branislav Lazic
Branislav Lazic

Reputation: 14826

You could add additional URL path prefix in your controller's @RequestMapping.

@RequestMapping(value = { "/products/{productId}", "/" })

This means that you can remove CommentsResource controller and you'll be able to access the same resource at:

/products/1/comments/5

and at

/comments/5

For instance, here:

 @GetMapping(value = "/comments/{commentNr}")
    public ResponseEntity<?> getComment(@PathVariable Long productId, @PathVaraible Long commentNr){
       // get comment with number commentNr of product productId
    }

The obvious issue would be a productId path variable. If you're using Java 8, that can be solved fairly easy by using Optional:

@GetMapping(value = "/comments/{commentNr}")
public ResponseEntity<?> getComment(@PathVariable Optional<Long> productId, @PathVaraible Long commentNr){
    // get comment with number commentNr of product productId
    // Check whether productId exists
}

Upvotes: 5

Related Questions