0
votes

I am trying to create user form in my application using Spring MVC and then validating the ModelAttribute. If my validations fail, I use the ModelAndView to return the view showing validation errors on form. My Controller code to render blank form is:

@GetMapping("/home")
public ModelAndView getUserHome(){
    ModelAndView mv = new ModelAndView();

    //In actual application, I have more code to put attributes in ModelAndView that will be needed
    //on view form, like select box options
    mv.addObject("user", new User());
    mv.setViewName("user/home/userHome");
    return mv;
}

Once the user form on view is filled in, and submit button clicked, I POST the request to the following Controller method:

@PostMapping("/persistUser")
public ModelAndView saveUser(@ModelAttribute("user") @Validated User user, BindingResult bindingResult, ModelMap modelMap){

    ModelAndView mv = new ModelAndView();

    List<ObjectError> errors = bindingResult.getAllErrors();

    if(bindingResult.hasErrors() && !(errors.size() == 1 && errors.get(0).getCode().equals("user.system.mismatch"))){

        mv.addObject("user", user);
        //more code to put more attributes like select box options on view
        mv.setViewName("user/home/userHome");
        return mv;
    }

    //if no errors, I have code to persist user object here. At this point
    //I sure can have userId and pass this as request attribute to redirect URL.
    //But, I need to also pass some messages(based on business logic) about what selections were
    //made on user form. I do not have the option to persist these selections on backend,
    //as our database does not have supporting columns for that. Therefore, I need to send them as 
    //redirect attributes. And these messages would be displayed only once, right after the user 
    //object was created. Upon subsequent visits to this userId view, these messages would no longer 
    //be required. So we don't really need to persist them at backend.

    RedirectView rv = new RedirectView("/user/home?userId="+user.getId(), true, true, false);
    //Here user.getId() is the userId newly persisted.
    modelMap.addAttribute("message", messageSource.getMessage("user.system.mismatch", null, Locale.US));

    return new ModelAndView(rv, modelMap);
}

I get the "user" model object here, perform validations, and if errors, I send user back to the form via ModelAndView. If no errors, I have code to persist object and redirect to another view that shows the user object that was just created. On this view, I need to display some messages which I am trying to pass as redirect attributes to the redirect receiving controller method. Please read the commented lines that explain why I need to pass the messages. I wrote my Controller method as follows:

@GetMapping("/user/home")
public ModelAndView getUser(
                        @RequestParam(name="userId", required = true) String userId,
                        @RequestParam(name="message", required = false) String message,
                        HttpSession session){
    ModelAndView mv = new ModelAndView();

    User user = (User)session.getAttribute("user");

    mv.addObject("message", message);
    mv.setViewName("user/home/view");
    return mv;
}

I have not been able to retrieve the "message" attribute here. I have tried using RedirectAttributes too, but either way I am not able to retrieve the values stored in "message". Of course I can send "message" as another request parameter, but my messages tend to be long and I don't want to see super long URLs.

Can somebody please advice how do I tackle this problem? Thanks.

1
A RedirectAttribute will be restored as part of the model. So no you won't be able to access it using @RequestParam. It is already part of the implicit model. I suggest to stop using ModelAndView and just inject what you need. If you hadd a Model or ModelMap as method argument instead, it will already contain the message attribute.M. Deinum
@Deinum You mean, I should try something like @GetMapping("/user/home") public ModelAndView getUser(ModelMap modelMap, HttpSession session){ //then how can I retrieve message from ModelMap here? //I can't find any method like modelMap.get("message") }Monica Lavale
ModelMap is Map and has all the methods that a regular Map has.M. Deinum

1 Answers

0
votes

I finally figured a way to add "message" as RedirectAttributes and later retrieve it as ModelAttribute. Below are the modified version of controller methods:

@PostMapping("/persistUser")
public ModelAndView saveUser(@ModelAttribute("user") @Validated User user, BindingResult bindingResult, RedirectAttributes ra){

ModelAndView mv = new ModelAndView();

List<ObjectError> errors = bindingResult.getAllErrors();

if(bindingResult.hasErrors() && !(errors.size() == 1 && errors.get(0).getCode().equals("user.system.mismatch"))){

    mv.addObject("user", user);
    //more code to put more attributes like select box options on view
    mv.setViewName("user/home/userHome");
    return mv;
}

RedirectView rv = new RedirectView("/user/home?userId="+user.getId(), true, true, false);
//Here user.getId() is the userId newly persisted.
ra.addFlashAttribute("message", messageSource.getMessage("user.system.mismatch", null, Locale.US));

return new ModelAndView(rv);
}

And then the redirect receiving method is as follows:

@GetMapping("/user/home")
public ModelAndView getUser(
                    @RequestParam(name="userId", required = true) String userId,
                    @ModelAttribute(name="message") String message,
                    HttpSession session){
ModelAndView mv = new ModelAndView();

User user = (User)session.getAttribute("user");

mv.addObject("message", message);
mv.setViewName("user/home/view");
return mv;
}

This works as expected for me.