Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preserving model state with Post/Redirect/Get pattern

At the moment I am trying to implement the Post/Redirect/Get pattern with Spring MVC 3.1. What is the correct way to preserve and recover the model data + validation errors? I know that I can preserve the model and BindingResult with the RedirectAttributes in my POST method. But what is the correct way of recovering them in the GET method from the flash scope?

I have done the following to POST:

    @RequestMapping(value = "/user/create", method = RequestMethod.POST)
public String doCreate(@ModelAttribute("user") @Valid User user, BindingResult result, RedirectAttributes rA){

    if(result.hasErrors()){
        rA.addFlashAttribute("result", result);
        rA.addFlashAttribute("user", user);

        return "redirect:/user";
    }

    return "redirect:/user/success";
}

And the following to GET the user creation form:

    @RequestMapping(value = "/user", method = RequestMethod.GET)
public ModelAndView showUserForm(@ModelAttribute("user") User user, ModelAndView model){

    model.addObject("user", user);
    model.setViewName("userForm");

    return model;
}

This allows me to preserve the given user data in the case of an error. But what is the correct way of recovering the errors?(BindingResult) I'd like to show them in the form with the spring form tags:

<form:errors path="*" />

In addition it would be interesting how to access the flash scope from the get method?

like image 487
denis Avatar asked Feb 17 '12 14:02

denis


2 Answers

public class BindingHandlerInterceptor extends HandlerInterceptorAdapter {

    public static final String BINDING_RESULT_FLUSH_ATTRIBUTE_KEY = BindingHandlerInterceptor.class.getName() + ".flashBindingResult";

    private static final String METHOD_GET = "GET";
    private static final String METHOD_POST = "POST";


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


        if(METHOD_POST.equals(request.getMethod())) {

            BindingResult bindingResult = getBindingResult(modelAndView);

            FlashMap outFlash = RequestContextUtils.getOutputFlashMap(request);

            if(bindingResult == null || ! bindingResult.hasErrors() || outFlash == null ) {
                return;
            }

            outFlash.put(BINDING_RESULT_FLUSH_ATTRIBUTE_KEY, bindingResult);

        }

        Map<String, ?> inFlash = RequestContextUtils.getInputFlashMap(request);

        if(METHOD_GET.equals(request.getMethod()) && inFlash != null && inFlash.containsKey(BINDING_RESULT_FLUSH_ATTRIBUTE_KEY)) {

            BindingResult flashBindingResult = (BindingResult)inFlash.get(BINDING_RESULT_FLUSH_ATTRIBUTE_KEY);

            if(flashBindingResult != null) {

                BindingResult bindingResult = getBindingResult(modelAndView);

                if(bindingResult == null) {
                    return;
                }

                bindingResult.addAllErrors(flashBindingResult);

            }

        }

    }

    public static BindingResult getBindingResult(ModelAndView modelAndView) {

        if(modelAndView == null) {
            return null;
        }

        for (Entry<String,?> key : modelAndView.getModel().entrySet()) {
            if(key.getKey().startsWith(BindingResult.MODEL_KEY_PREFIX)) {
                return (BindingResult)key.getValue();
            }
        }

        return null;
    }


}
like image 122
Ilyes Ben Hassen Avatar answered Oct 18 '22 11:10

Ilyes Ben Hassen


Why don't you show the update form after the binding fails, so the user can try to resubmit the form?

The standard approach for this seems to be to return the update form view from the POST handler method.

if (bindingResult.hasErrors()) {
  uiModel.addAttribute("user", user);
  return "user/create";
}

You can then display errors with the form:errors tag.

like image 1
phahn Avatar answered Oct 18 '22 10:10

phahn