0
votes

I have a Spring MVC web application with conroller like below :

@Controller
public class ActionRestController {
@RequestMapping(value = "/list", method = GET)
@ResponseBody
public List<Action> list(Action action, SearhCriteria searchCriteria) {
     List<Action> ret = new ArrayList<Action>();
     // Call a service method to get the records
     // Copy the records into the list
     // return the list of objects
     return ret;
}

The Controller is invoked when the user does a search. There are several such controllers in the app, one for each searchable entity. For reasons that I cannot explain very well, here, I cannot modify these controllers in anyway.

But now, I have requirement in the UI to display the search criteria and the no. of records and paging details, as well. This information is not returned by the controller. The JSON returned by the Controller contains just the list of records.

I have put up a different controller which will handle the request, gets and puts the extra info in the model and forwards the request to the existing controller like below :

@Controller
public class ActionExtendedController {
@RequestMapping(value = "/searchlist", method = GET)
@ResponseBody
public List<Action> list(Action action, SearhCriteria searchCriteria, Model model) {
     model.addAttribute("searchParameters", searchCriteria);
     return "forward:/list";
}

Upto this point, all is well. What I want to do is intercept the request at a point where the List is returned from the controller, before it is converted to JSON, and return a map containing the list and the search parameters.

Now since the 'immutable' controller users ResponseBody the control goes to the JacksonMessageConverter amd the response goes out from there. I have already tried the following paths and they do not work.

  • Interceptor - By the time I get here, the response is already written out, so there is no way to change it.

  • Custom ObjectMapper for the JasksonMessageConverter - Will not work, since I do not have access to the model object inside the mapper, I only have access to the list returned by the controller.

  • Aspect @After pointcut for the controller - I think this technique will work, but I cannot get it to work. The advise does not fire and I am sure I am missing something in the configuration.

Is there a way to get Spring AOP to fire on a annotated controller, handler method or can anyone suggest another method of intercepting the handler return value (along with the model) ?

2

2 Answers

0
votes

How about a simple delegation to the base controller in your extended controller:

@Controller
public class ActionExtendedController {
    @Autowired ActionRestController baseRestController;
    @Autowired MappingJacksonJsonView mappingJacksonJsonView;

    @RequestMapping(value = "/searchlist", method = GET)
    public View list(Action action, SearhCriteria searchCriteria, Model model) {
        List<Action> actions = baseRestController.list(action, searchCriteria, model);
        model.addAttribute("actions", actions);
        model.addAttribute("searchParameters", searchCriteria);
        return mappingJacksonJsonView;
    }

this way you are delegating to the original controller, but using this new controller for the view. Just register a mappingJacksonJsonView as a bean also which will serialize all model objects (searchcriteria and actions) into the json view. You need not even return a view but can also use @ResponseBody, with a type that can hold the responses and search criteria.

0
votes

Why don't you change the return type to a Map? Like:

@Controller
public class ActionRestController {
@RequestMapping(value = "/list", method = GET)
@ResponseBody
public Map<String, Object> list(Action action, SearhCriteria searchCriteria) {  
    Map<String, Object> map = new HashMap<String, Object>();     
    List<Action> ret = new ArrayList<Action>();
     // Call a service method to get the records
     // Copy the records into the list
     // return the list of objects
    map.put("searchResult",ret);
    map.put("searchCriteria", searchCriteria);
    return map;
}