Could Spring MVC call @ModelAttribute after @RequestMapping?
Asked Answered
A

2

7

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?

Arlenaarlene answered 26/7, 2013 at 3:0 Comment(0)
R
5

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.

Rosalba answered 26/7, 2013 at 14:29 Comment(0)
P
2

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)

Precincts answered 26/7, 2013 at 14:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.