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) { .... }
}
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.