Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Could Spring MVC call @ModelAttribute after @RequestMapping?

I have a controller like this:

@Controller
public class HomeController {

    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public String update(@RequestParam("user") User user, ModelMap model){
        SaveUserToDatabase(user);
        return "index";
    }

    @ModelAttribute("user")
    String getUser() {
        return LoadCurrentUserFromDataBase();
    }
}

In short, my views would render user in almost every actions in HomeController, but I don't want to code:

model.addAttribute("user", LoadCurrentUserFromDataBase())

in every actions, instead I'm seeking a way like @ModelAttribute to expose user to all my views.

However, according to the docs, @ModelAttribute methods in a controller are invoked before @RequestMapping methods, within the same controller.

As to my code, getUser is called before update, but i'd like to get the updated user.

Is there a way to expose the user attribute after actions without explicitly call model.addAttribute in every actions?

like image 514
marstone Avatar asked Jul 26 '13 03:07

marstone


2 Answers

Each time you do a POST, make a redirection. That way, your POST method will only be responsible for updating the data, and the updated data will be loaded by the target controller.

So in this case, the update() method would redirect to another controller which would call the getUser() method before its GET method.

like image 165
WilQu Avatar answered Sep 24 '22 03:09

WilQu


The Post / redirect / GET solution is valid if it works for you.

However, I had a similar situation where I had model attributes that needed to be written by all my controllers, after request processing (tracking information mostly). What I did was to register a custom interface (e.g. AdditionalModelDataSupplier), which I apply to all controllers that need to provide additional data. The interface would have a method like this:

void provideAdditionalData(Model model, HttpServletRequest request);

Now, I wrote an interceptor that in the postHandle method checks the Controller bean for this interface and calls this method:

@Override
public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler,
        final ModelAndView modelAndView) throws Exception {

    AdditionalModelDataSupplier modelDataSupplier = findAdditionalModelDataSupplier(handler);
    if (modelDataSupplier != null) {
        final ModelMap modelMap = modelAndView.getModelMap();
        final Model targetModel;
        if (modelMap instanceof Model) {
            targetModel = (Model) modelMap;
        } else {

            // the modelmap doesn't implement model, so we need to provide a wrapper view
            targetModel = new ForwardingModel(modelMap);
        }

        modelDataSupplier.provideAdditionalData(targetModel, request);
    }
}
@Nullable
private static AdditionalModelDataSupplier findAdditionalModelDataSupplier(final Object handler) {
    if (handler instanceof AdditionalModelDataSupplier) {
        return (AdditionalModelDataSupplier) handler;
    }

    if (handler instanceof HandlerMethod) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Object bean = handlerMethod.getBean();
        if (bean instanceof AdditionalModelDataSupplier) {
            return (AdditionalModelDataSupplier) bean;
        }
    }

    return null;
}

(the ForwardingModel class mentioned above is trivial to create, it just delegates everything to the ModelMap)

like image 21
Sean Patrick Floyd Avatar answered Sep 20 '22 03:09

Sean Patrick Floyd