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?
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"; } ...... }
*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:
- Annotate your bean (eg. @NotEmpty, @Pattern, @Length, @Email etc.)
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"; } // ....
}
Update your view/jsp to hold error messages:
This article can surely be helpful.
You can change your POST implementation to this:
@RequestMapping(value = "/buy/{buyId}", method = RequestMethod.POST) public String buyPost(@PathVariable long buyId, @Valid @ModelAttribute("buyForm") BuyForm buyForm, BindingResult result) { buyForm.setId(buyId); // important to do this also in the error case, otherwise, // if the validation fails multiple times it will not work. if (result.hasErrors()) { byForm.setId(buyId); return "/buy/{buyId}"; } buyService.buy(buyForm); return "redirect:/show/{buyId}"; }
Optionally, you can also annotate the method with @PostMapping("/buy/{buyId}")
if you use Spring 4.3 or higher.