16

I am stuck trying to passing the BindingResult through RedirectionAttributes:

I have referenced Spring - Redirect after POST (even with validation errors), but am still stuck.

I have a GET method:

@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test(@ModelAttribute("exampleForm") final ExampleForm exampleForm, final Model model)
{
    return "test";
}

and a POST method:

@RequestMapping(value = "/doSomething", method = RequestMethod.POST)
public String doSomething(@Valid @ModelAttribute("exampleForm") final ExampleForm exampleForm, final BindingResult bindingResult, final RedirectAttributes redirectAttributes)
{
    if (bindingResult.hasErrors())
    {
        redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.exampleForm", bindingResult);
        redirectAttributes.addFlashAttribute("exampleForm", exampleForm);
        return "redirect:/test";
    }
}

However, I am not seeing binding errors in the model of the GET method (after redirect) when I receive them in the POST method - they seem to disappear.

Here is the bindingResult object in the POST method: org.springframework.validation.BeanPropertyBindingResult: 1 errors

Here is the model entry in the GET method showing 0 errors: org.springframework.validation.BindingResult.exampleForm=org.springframework.validation.BeanPropertyBindingResult: 0 errors

Any help would be much appreciated.

Community
  • 1
  • 1
jared
  • 337
  • 1
  • 2
  • 9
  • Possible duplicate of [Spring - Redirect after POST (even with validation errors)](http://stackoverflow.com/questions/2543797/spring-redirect-after-post-even-with-validation-errors) – Eric Dec 09 '15 at 14:18

4 Answers4

20

This is similar to Cyril Deba's answer, but you can do it with flash attributes rather than @SessionAttributes. The key is that it looks like if you put the @ModelAttribute to make the form on the GET RequestMapping method, it doesn't keep the data from the redirect flash attributes, but if you make the new form object to go into the model as its own method, then it only uses it if the model doesn't already contain it. I yet haven't found documentation from Spring that says it works this way, or why they seem to work different, but something like the following works:

@Controller
public class MyFormController {

    @ModelAttribute("myForm")
    public MyForm newMyForm() {
        return new MyForm();
    }

    @RequestMapping(value = "/myForm/", method = RequestMethod.GET)
    public String showMyForm() {
        return "myForm";
    }

    @RequestMapping(value = "/myForm/", method = RequestMethod.POST)
    public String processMyForm(
            @Valid @ModelAttribute("myForm") final MyForm myForm,
            final BindingResult bindingResult,
            final RedirectAttributes redirectAttributes) {
        final String view;
        if (bindingResult.hasErrors()) {
            redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.myForm", bindingResult);
            redirectAttributes.addFlashAttribute("myForm", myForm);
            view = "redirect:/myForm/";
        } else {
            // Success
            view = "redirect:/";
        }
        return view;
    }
}
  • `redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.myForm", bindingResult);` - it is exactly works like sharm! You made my day! – bvn13 Oct 06 '19 at 18:52
6

This is by no means a good answer, but a work-around:

I had to set the flashAttribute to "exampleFormBindingResult", then override the model "org.springframework.validation.BindingResult.exampleForm" attribute

@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test(@ModelAttribute("exampleForm") final ExampleForm exampleForm, final Model model)
{
    if (model.asMap().containsKey("exampleFormBindingResult"))
    {
        model.addAttribute("org.springframework.validation.BindingResult.exampleForm",
                model.asMap().get("exampleFormBindingResult"));
    }

    return "test";
}

I added a flashAttribute with a the key "exampleFormBindingResult":

@RequestMapping(value = "/doSomething", method = RequestMethod.POST)
public String doSomething(@Valid @ModelAttribute("exampleForm") final ExampleForm exampleForm, final BindingResult bindingResult, final RedirectAttributes redirectAttributes)
{
    if (bindingResult.hasErrors())
    {
        redirectAttributes.addFlashAttribute("exampleFormBindingResult", bindingResult);
        redirectAttributes.addFlashAttribute("exampleForm", exampleForm);
        return "redirect:/test";
    }
}

I hope someone else finds a better answer.

jared
  • 337
  • 1
  • 2
  • 9
  • 2
    like you said this is a good work around, have you found a better answer yet? I still can't figure out why the BindingResult is getting overrided when the redirection occurs (showing 0 errors). – dacuna Feb 22 '14 at 05:58
  • It is a work around but I had the same code as the OP and this was the only thing that worked. – Vlad G. Sep 11 '19 at 15:32
4

Your GET method should like

@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test(final Model model)
{
    if(!model.containsAttribute("exampleForm")){
        model.addAttribute("exampleForm", new ExampleForm());
    }
    return "test";
}
Qingfei Wei
  • 49
  • 1
  • 3
3

One more approach that may resolve the issue. Session attributes help to persist objects between requests, so the following based on it

@Controller
@SessionAttributes(
{ "exampleForm" })
public class HomeController
{

@ModelAttribute("exampleForm")
public ExampleForm getExampleForm()
{
    return new ExampleForm();
}

@RequestMapping(value = "/myform", method = RequestMethod.POST)
public String proccessForm(@Valid @ModelAttribute("exampleForm") 
        final ExampleForm form, final BindingResult result,
        final SessionStatus sessionStatus)
{
    if (result.hasErrors())
    {
        return "redirect:/myform";
    }
    sessionStatus.setComplete();
    return "redirect:/complete";
}

@RequestMapping(value = "/myform", method = RequestMethod.GET)
public String showForm(final Model model)
{
    return "form";
}

@RequestMapping(value = "/complete", method = RequestMethod.GET)
public String showSomething(final Model model)
{
    return "complete";
}
}
Cyril Deba
  • 1,200
  • 2
  • 16
  • 30