Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpringMVC controller: how to stay on page if form validation error occurs

I have next working code in my SpringMVC controller:

@RequestMapping(value = "/register", method = RequestMethod.GET)
public void registerForm(Model model) {
    model.addAttribute("registerInfo", new UserRegistrationForm());
}

@RequestMapping(value = "/reg", method = RequestMethod.POST)
public String create(
        @Valid @ModelAttribute("registerInfo") UserRegistrationForm userRegistrationForm,
        BindingResult result) {

    if (result.hasErrors()) {
        return "register";
    }
    userService.addUser(userRegistrationForm);
    return "redirect:/";
}

In short create method try to validate UserRegistrationForm. If form has errors, it leaves user on the same page with filled form fields where error message will be shown.

Now I need to apply the same behaviour to another page, but here I have a problem:

@RequestMapping(value = "/buy/{buyId}", method = RequestMethod.GET)
public String buyGet(HttpServletRequest request, Model model, @PathVariable long buyId) {
    model.addAttribute("buyForm", new BuyForm());
    return "/buy";
}

@RequestMapping(value = "/buy/{buyId}", method = RequestMethod.POST)
public String buyPost(@PathVariable long buyId,
                          @Valid @ModelAttribute("buyForm") BuyForm buyForm,
                          BindingResult result) {

    if (result.hasErrors()) {
        return "/buy/" + buyId;
    }

    buyForm.setId(buyId);
    buyService.buy(buyForm);
    return "redirect:/show/" + buyId;
}

I faced with issue of dynamic url. Now if form has errors I should specify the same page template to stay on current page, but also I should pass buyId as a path variable. Where are a conflict in this two requirements. If I leave this code as is, I get an error (I'm using Thymeleaf as a template processor):

Error resolving template "/buy/3", template might not exist or might not be accessible by any of the configured Template Resolvers

I can write something like return "redirect:/buy/" + buyId, but in this case I lose all data and errors of form object.

What should I do to implement in buyPost method the same behaviour as in create method?

like image 708
Maxim Kolesnikov Avatar asked Aug 08 '13 22:08

Maxim Kolesnikov


People also ask

What is the use of @valid Annotation in spring boot?

The @Valid annotation ensures the validation of the whole object. Importantly, it performs the validation of the whole object graph. However, this creates issues for scenarios needing only partial validation. On the other hand, we can use @Validated for group validation, including the above partial validation.

How does Spring MVC provide support for validation?

The Spring MVC framework provides us with standard predefined validators to validate user input data in a simple and straightforward way. The Bean Validation API is the popular approach for data validations in Spring applications.


2 Answers

I tried the solution metioned in this post at this weekend, but it doesn't work for BindingResult.

The code below works but not perfect.

@ModelAttribute("command")
public PlaceOrderCommand command() {
    return new PlaceOrderCommand();
}

@RequestMapping(value = "/placeOrder", method = RequestMethod.GET)
public String placeOrder(
        @ModelAttribute("command") PlaceOrderCommand command,
        ModelMap modelMap) {
    modelMap.put(BindingResult.MODEL_KEY_PREFIX + "command",
            modelMap.get("errors"));
    return "placeOrder";
}

@RequestMapping(value = "/placeOrder", method = RequestMethod.POST)
public String placeOrder(
        @Valid @ModelAttribute("command") PlaceOrderCommand command,
        final BindingResult bindingResult, Model model,
        final RedirectAttributes redirectAttributes) {
    if (bindingResult.hasErrors()) {
        redirectAttributes.addFlashAttribute("errors", bindingResult);

        //it doesn't work when passing this          
        //redirectAttributes.addFlashAttribute(BindingResult.MODEL_KEY_PREFIX + "command", bindingResult);

        redirectAttributes.addFlashAttribute("command", command);
        return "redirect:/booking/placeOrder";
    }
    ......
}
like image 77
Yugang Zhou Avatar answered Oct 13 '22 10:10

Yugang Zhou


*I'm using Hibernate Validator APIs to validate my beans. To preserve form data along with displaying error messages, you need to do these 3 things:

  1. Annotate your bean (eg. @NotEmpty, @Pattern, @Length, @Email etc.)

enter image description here

  1. Inside controller:

    @Controller public class RegistrationController {

    @Autowired
    private RegistrationService registrationService;
    
    @RequestMapping(value="register.htm", method=RequestMethod.GET, params="new")
    public String showRegistrationForm(Model model) {
        if (!model.containsAttribute("employee")) {
            model.addAttribute("employee", new Employee());
        }
        return "form/registration";
    }
    
    @RequestMapping(value="register.htm", method=RequestMethod.POST)
    public String register(@Valid @ModelAttribute("employee") Employee employee, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
        if (bindingResult.hasErrors()) {
            redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.employee", bindingResult);
            redirectAttributes.addFlashAttribute("employee", employee);
            return "redirect:register.htm?new";
        }
        registrationService.save(employee);
        return "workspace";
    }
    // ....
    

    }

  2. Update your view/jsp to hold error messages:

enter image description here

This article can surely be helpful.

like image 35
masT Avatar answered Oct 13 '22 10:10

masT