In my Spring MVC webapp I have a generic RESTful controller for CRUD operations. And each concrete controller had to declare only a @RequestMapping
, for example /foo
. Generic controller handled all request to /foo
and /foo/{id}
.
But now I need to write a bit more complex CRUD controller which will get additional request params or path variables, e.g /foo/{date}
and /foo/{id}/{date}
. So I extend my generic CRUD controller and write overloaded fetch(id, date)
method which will deal with both {id}
and {date}
. That is not a problem.
But I also need to 'disable' fetch(id)
implementation derived from base class (resource mustn't be available at /foo/{id}
anymore, only at /foo/{id}/{date}
). The only idea I came up with is to override this method in my concrete controller, to map it on a fake uri and return null
. But this looks like rather ugly dirty hack because we expose some fake resource uri, instead of disabling it. May be there is a better practice?
Any ideas?
//My generic CRUD controller
public abstract class AbstractCRUDControllerBean<E, PK extends Serializable> implements AbstractCRUDController<E, PK> {
@RequestMapping(method=RequestMethod.GET)
public @ResponseBody ResponseEntity<E[]> fetchAll() { ... }
@RequestMapping(value="/{id}", method=RequestMethod.GET)
public @ResponseBody ResponseEntity<E> fetch(@PathVariable("id") PK id) { ... }
@RequestMapping(method=RequestMethod.POST)
public @ResponseBody ResponseEntity<E> add(@RequestBody E entity) { ... }
@RequestMapping(value="/{id}", method=RequestMethod.PUT)
public @ResponseBody ResponseEntity<E> update(@PathVariable("id") PK id, @RequestBody E entity) { ... }
@RequestMapping(value="/{id}", method=RequestMethod.DELETE)
public @ResponseBody ResponseEntity<E> remove(@PathVariable("id") PK id) { .. }
}
.
//Concrete controller, working with Foo entities
@Controller
@RequestMapping("/foo")
public class FooControllerImpl extends
AbstractCRUDControllerBean<Foo, Long> implements FooController {
//ugly overriding parent's method
@RequestMapping(value="/null",method=RequestMethod.GET)
public @ResponseBody ResponseEntity<Foo> fetch(@PathVariable("id") PK id) {
return null;
}
//new fetch implementation
@RequestMapping(value="/{id}/{date}", method=RequestMethod.GET)
public @ResponseBody ResponseEntity<Foo> fetch(@PathVariable("id") PK id, @PathVariable("date") Date date) { .... }
}
To provide a basic infrastructure, all of Spring's various Controller inherit from AbstractController , a class offering caching support and, for example, the setting of the mimetype.
@Controller is used to mark classes as Spring MVC Controller. @RestController annotation is a special controller used in RESTful Web services, and it's the combination of @Controller and @ResponseBody annotation. It is a specialized version of @Component annotation.
In Spring MVC, controller methods are the final destination point that a web request can reach. After being invoked, the controller method starts to process the web request by interacting with the service layer to complete the work that needs to be done.
Are you trying to achieve the resource, subresource type of jersey using spring? That may not be directly possible. Instead of declaring the generic RESTful service as controller, why don't you delegate it to them?
//My generic CRUD Operations
public abstract class AbstractCRUDControllerBean<E, PK extends Serializable> implements AbstractCRUDController<E, PK> {
public ResponseEntity<E[]> fetchAll() { ... }
public ResponseEntity<E> fetch(@PathVariable("id") PK id) { ... }
public ResponseEntity<E> add(@RequestBody E entity) { ... }
public ResponseEntity<E> update(@PathVariable("id") PK id, @RequestBody E entity) { ... }
public ResponseEntity<E> remove(@PathVariable("id") PK id) { .. }
}
and delegate in the controller.
//Concrete controller, working with Foo entities
@Controller
@RequestMapping("/foo")
public class FooControllerImpl extends
AbstractCRUDControllerBean<Foo, Long> implements FooController {
//we are interested in using fetchall but not others
@RequestMapping(method=RequestMethod.GET)
public @ResponseBody ResponseEntity<Foo> fetch(@PathVariable("id") PK id) {
return fetchAll();
}
//fetch with id and date
@RequestMapping(value="/{id}/{date}", method=RequestMethod.GET)
public @ResponseBody ResponseEntity<Foo> fetch(@PathVariable("id") PK id, @PathVariable("date") Date date) { .... }
}
also, you can map method based on the availability of the parameters too,
@RequestMapping(value="/{id}/{date}", params={"param1","param2","!param3"})
public @ResponseBody ResponseEntity<E> customFetch(@PathVariable("id") PK id,
@PathVariable("date") Date date, @RequestParam("param1") String param1,
@RequestParam("param2") String param2) {...}
This method maps /foo/id/date when param1 and param2 exists and param3 does not exist.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With