In my Spring Boot application I have an "edit user" form, the form is bound to a SecurityUser bean, but has an additional (non-bean) field for confirming the password. The GET mapping in my controller looks like this:
@GetMapping("/security/user/{username}")
public String showEditUserForm(@PathVariable("username") String username, Model model) {
model.addAttribute("securityUser",securityUserService.findByUsername(username));
return "/security/edituser";
}
In the POST mapping, I want to check the password confirmation input's value and compare it to the password field value, so I coded it like this:
@PostMapping("/security/user/{username}")
public String processEditUser(@Valid SecurityUser securityUser, @RequestParam String confirmPassword, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) {
return "security/edituser";
}
logger.debug(securityUser.toString());
logger.debug(confirmPassword);
redirectAttributes.addFlashAttribute("flashstatus","success");
redirectAttributes.addFlashAttribute("flashmessage","You successfully submitted an edituser form");
return "redirect:/security/success";
}
If the form is valid, everything works fine (granted, it's just logging and a redirect to a success page). But if any form field is invalid, a 405 error (HTTP method not supported) is the result.
In the logs it's:
2020-02-06 15:44:39.114 WARN 20496 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
Originally the GET and POST endpoints were different, so I made them the same, as you can see, and that obviously wasn't the solution. If I eliminate the @BindingResult variable from the POST mapping, the bug goes away, but then I obviously can't check the password confirmation.
How can I access a form input that's not a bean field in a Spring Boot POST mapping without this error occurring?
This is one of the rare cases where the order of the argument actually matters. The BindingResult
parameter has to be right after the object you want to validate.
From the documentation:
You must declare an Errors, or BindingResult argument immediately after the validated method argument.