Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring MVC controller inheritance and routing

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

}
like image 626
Nofate Avatar asked Sep 12 '11 12:09

Nofate


People also ask

Which is inheritance class of spring controller?

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.

What is difference between controller and rest controller in spring?

@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.

What is the role of controller in Spring MVC?

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.


1 Answers

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.

like image 64
kalyan Avatar answered Sep 20 '22 11:09

kalyan