400 Bad request when validation Model Attribute with Spring MVC - spring

I am writing a simple controller that accepts request on user sign-up I want to validate the Model Attribute but I receive 400 bad request.
I've checked this question here and is pretty much the problem I have but the solutions does not work for me.
#RequestMapping(method = RequestMethod.POST, value = "/sign-up", consumes = "application/x-www-form-urlencoded")
public ModelAndView addUser(ModelMap model,#Valid #ModelAttribute
UserRegistrationInfo userRegistrationInfo,
HttpServletRequest httpRequest,
HttpSession httpSession,
BindingResult result) { ..... }
EDIT
Spring Version: 4.0.6.RELEASE
I've read SpringMVC architecture and set a break points at DefaultHandlerExceptionResolverclass, doResolveException method and found that a BindException exception is thrown.However, I did not know why BindingResult is not filled and method execution is not called to let me determine what behavior I want ? the execution ends up at return handleBindException((BindException) ex, request, response, handler); which is
protected ModelAndView handleBindException(BindException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { response.sendError(HttpServletResponse.SC_BAD_REQUEST); return new ModelAndView(); }

As you've rightly figure out, you're getting a bad request on account of the binding errors. All the info that you need are in fact contained in the BindingResult object but in order to use it properly you should set your BindingResult to immediately follow your ModelAttribute e.g.
#RequestMapping(method = RequestMethod.POST, value = "/sign-up", consumes = "application/x-www-form-urlencoded")
public ModelAndView addUser(ModelMap model,#Valid #ModelAttribute
UserRegistrationInfo userRegistrationInfo, BindingResult result
HttpServletRequest httpRequest,
HttpSession httpSession,
) { ..... }
While ordering of arguments generally doesn't matter, BindingResult parameter is an exception, as the method can contain several ModelAttribute parameters, each having its own dedicated instance of BindingResult. In this case the association is established by having the BindingResult parameter immediately follow the ModelAttribute it applies to.
When you reorder your parameters you will no longer get the 400 error, rather the request will enter the controller, and the logs will show the exact binding problem, or you simply check if result.hasErrors() and iterate through field errors by calling result.getAllErrors(). Then it should be simple enough to resolve your binding issue and the consequent bad request.
Check the section od the doc http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-methods

Related

SpringMVC Controller Method Doesn't Bring in Model

I noticed that when a Form Submit happens, my handler method that captures a #Model object as a parameter does have a valid Model available to me, e.g.
public ModelAndView save(final HttpServletRequest request,
#ModelAttribute(MODEL_NAME) MyModel model,
BindingResult bindingResult)
But when I have a handler method corresponding to a URL redirect from a simple <a href=".."> button/link, e.g.
<a href="myController.do&function=add">
leading to
#RequestMapping(params = "function=add")
public ModelAndView add(final HttpServletRequest request,
#ModelAttribute(MODEL_NAME) MyModel model) throws Exception
in that case, the model object is NULL. The model wasn't carried through the request and made available to me in the handler method.
Is there a way to reexpose the model? I do have the same param in both methods, but in the 2nd one the "model" object is NULL.
If you want to use GET request with you will need to add the model to the URL. Or you can use POST with a form instead.
See: how to pass a java object from jsp to spring controller on clicking a link

#RequestBody not restricting to POJO type and BindingResult hasErrors always false

I was not experiencing this problem early in development but just noticed that this was happening when debugging another problem. This happens on all REST endpoints, but below is an example:
#RestController
#RequestMapping("/editlisting")
public class EditParkingSpaceListingController {
#Autowired
ParkingSpaceRepository parkingSpaceRepository;
#Autowired
ParkingSpaceListingRepository parkingSpaceListingRepository;
#RequestMapping(method = RequestMethod.PUT)
public ResponseEntity<String> editParking(#RequestBody ParkingSpaceListingClient pslc, BindingResult result) {
if (result.hasErrors()) {
return new ResponseEntity<String>("", HttpStatus.BAD_REQUEST);
}
// Code to save pslc data to database.
Now, if I send an HTTP request with the body as
{ }
I get a 200 response and when I check MongoDB, there is a new empty document in the collection. If I send an empty body with no brackets, as expected it will return 400. If I send a body with random garbage data that does not exist in the POJO, BindingResult does not seem to pick up the error and a new blank document is still created.
You need to follow the below steps for the input document validations:
(1) Add the javax.validation package constraints (like #NotNull, #Size, etc..) to your ParkingSpaceListingClient bean class.
(2) Add #Validated annotation to your controller method, to capture the validation errors into BindingResult object.
You can look here for more details on Input Validations.

optional parameter in Spring MVC method

I am learning spring MVC and come across these methos in spring contrller MVC 3.1
ControllerClass(){
#RequestMapping(....)
public String show( Model uiModel) {
return ".....";
}
#RequestMapping(value = "/{id}", params = "form", method = RequestMethod.POST)
public String update(#Valid Contact contact, BindingResult bindingResult, Model uiModel,
HttpServletRequest httpServletRequest, RedirectAttributes redirectAttributes, Locale locale,
#RequestParam(value="file", required=false) Part file) {
if (bindingResult.hasErrors()) {
...........
return ".....";
}
parameters like BindingResult , Model ,
HttpServletRequest , RedirectAttributes , Locale ,
#RequestParam(value="file", required=false) Part are optional but I wonder where I can find these optional parameter and under which situation it can appear in method.
Parameter:
BindingResult - imagine you have an registration-form and you would pre validate the user input, then you can use the BindingResult.
Model - After the user is registered, he wants to edit his own profile he goes to a edit site, in this site you would show the data from the user. Here you can search for the user and add the user-object to the model and in the template you can read the values from the model-attribute.
HttpServletRequest provides request information.
#RequestParam(value="file", required=false) from Spring:
annotated parameters for access to specific Servlet request parameters. Parameter values are converted to the declared method argument type
Imagine you have a table of users and you would edit one of these, you select an entry and there you can send the userId as a requestparam.
There is a similar attribute, it's called #PathVariable the main difference, the #PathVariable is mandatory. The #RequestParam is optional respectively for this exist a "fallback/default value".
The #PathVariable is a part from the url:
#RequestMapping(value = "/{login}/edit", method = RequestMethod.GET)
public ModelAndView editUserByLogin(#PathVariable("login") final String login, final Principal principal) {}
The other two I have not used yet.

Spring preprocess request in another controller method

In one of my controllers I have:
#RequestMapping(value = "search", method = RequestMethod.GET)
public ModelAndView searchUsers(HttpSession session, HttpServletRequest request) {
UiUserSearchCriteria userSearchCriteria = (UiUserSearchCriteria) session
.getAttribute("UsersController_userSearchCriteria");
if (null == userSearchCriteria) {
userSearchCriteria= defaultUserSearchCriteria;
}
// Here be dragons
return searchUsers(userSearchCriteria, new BeanPropertyBindingResult(userSearchCriteria,
"userSearchCriteria"), session, request);
}
#RequestMapping(value = "search", method = RequestMethod.POST)
public ModelAndView searchUsers(
#ModelAttribute("userSearchCriteria") UiUserSearchCriteria userSearchCriteria,
BindingResult bindingResult, HttpSession session, HttpServletRequest request) {
userSearchCriteriaValidator.validate(userSearchCriteria, bindingResult);
if (bindingResult.hasErrors()) {
// Here be dragons
return new ModelAndView("searchUsers");
}
ModelAndView result = new ModelAndView("redirect:listUsers");
PagedListHolder<UiUser> userList = new PagedListHolder<UiUser>(
usersService.searchUsers(userSearchCriteria));
userList.setPageSize(10);
userList.setSort(defaultSort);
userList.resort();
session.setAttribute("UsersController_userList", userList);
session.setAttribute("UsersController_userSearchCriteria", userSearchCriteria);
return result;
}
The logic is simple: when the user navigates to search page I actually perform a search with default criteria and return him a list (this is the result of requirements changing, huh).
I found a problem in this code, accidentally. When default search criteria is not valid the behavior is: navigate to search -> populate search criteria with invalid criteria -> call another method (the second one, with POST) -> perform validation -> errors are not empty, so return searchUsers view. But the BindingResult bindingResult is actually syntethic, from previous method (new BeanPropertyBindingResult(userSearchCriteria, "userSearchCriteria")). So I got an error No binding result is bound to session (I agree with this).
I cannot have #ModelAttribute and BindingResult parameters (that, which are bound by Spring) pair in GET method to call POST with them.
So what is the best solutions for this?
I think you can simply associate your new BeanPropertyBindingResult(userSearchCriteria,
"userSearchCriteria") with an appropriate Spring model attribute name, this way:
BindingResult bindingResult = new BeanPropertyBindingResult(userSearchCriteria, "userSearchCriteria")
model.addAttribute(BindingResult.MODEL_KEY_PREFIX + "userSearchCriteria", bindingResult);
This is the default Spring MVC behavior of binding the validation results of a specific model attribute and should help you avoid the No binding result.. error

What does it mean to say "if (binding.hasErrors())" in Spring?

In this example, I don't understand what the BindingResult is for and what it means to say if (binding.hasErrors()) below.
#RequestMapping(value = "/test", method = RequestMethod.POST)
public final String submit(#ModelAttribute(TEST) #Valid final Test test, final BindingResult binding,
final HttpServletRequest request, final ModelMap modelMap)
{
if (binding.hasErrors())
{
return "test";
}
BindingResult is a data binding result associated with the previous argument (that is, test). It holds information about the errors of binding request parameters to the properties of test, such as type mismatches. When #Valid annotation is present, it also holds errors produced by the authomatic validation of test.
So, binding.hasErrors() determines whether errors was found during binding and validation of the test. When such errors present, the typical behaviour is to redisplay the form with errors messages.

Resources