Spring form ModelAttribute field validation to avoid 400 Bad Request Error
Asked Answered
S

1

6

I've got an ArticleFormModel containing data sent by normal html form which is injected by Spring using @ModelAttribute annotation, i.e.

@RequestMapping(value="edit", method=RequestMethod.POST)
public ModelAndView acceptEdit(@ModelAttribute ArticleFormModel model, 
    HttpServletRequest request, BindingResult errors)
{
    //irrelevant stuff
}

Everything works perfectly fine up to some point. The problem is that the ArticleFormModel contains a double field (protected, set using normal setter). Everything works fine as long as data sent by user is a number. When they type a word, all I get is 400 Bad Request Http Error.

I've already registered a WebDataBinder for this controller

@InitBinder
protected void initBinder(WebDataBinder binder) throws ServletException
{
    binder.setValidator(validator);
}

where validator is an instance of a custom class implementing org.springframework.validation.Validator interface but I don't know what to do next. I'd like to be able to parse the model, get valid HTTP response and display error message in the form. The initBinder() method is called and I can call validator.validate() from it but it doesn't change the error (for that wrong data).

I'm aware that I could use a setter to parse the string, check if it's a number, if not, store that info in a variable, then retrieve that variable during validation, but that seems to be too much work. There has to be an easier way to force a type on the field without getting an error. Also, the issue is in data binding, not validation, so I feel that it should be placed in the respective code layer.

I was also thinking about implementing java.beans.PropertyEditor and calling binder.registerCustomEditor(), but I'm lacking a reliable knowledge source.

Client-side validation (checking if data is number via JavaScript) isn't a possibility.

TL;DR:

How can I force a field to be of specific type for a @ModelAttribute item without getting 400 Bad Request Http Error?

Sori answered 19/8, 2013 at 22:2 Comment(5)
Show your ArticleFormModel class.Amil
@Sotirios Delimanolis It's a normal Java Bean containing auto-generated getters and setters and an empty constructor. It extends a Hibernate entity. The field I'm talking about is protected double price. Everything works fine as long as form item for price contains only digits.Sori
This is expected Spring behavior. If you enter an invalid format (ex. "3.14abc" which isn't a double), then that's a Bad Request, Invalid Syntax error. You can always put @RequestParam for each form field and parse them/handle errors yourself. If you're going to use @ModelAttribute, you have to adapt to Spring's behavior.Amil
@SotiriosDelimanolis I understand that, but I'd like to show the user a form with error messages (preferably using <form:error> instead of an error page. Is this possible without handling error pages?Sori
You shouldn't do it that way and it is impossible with @ModelAttribute. If your client is html, use something like <input type="number" .../>. Unless you change your field to String` and use some either custom or existing annotation to validate it to a number. You will then have to cast the String when you need to use it as a double.Amil
W
20

You can use <form:errors> for a binding error.

It looks like this:

Controller:

@RequestMapping(value="edit", method=RequestMethod.POST)
public ModelAndView acceptEdit(@ModelAttribute ArticleFormModel model, 
    BindingResult errors, HttpServletRequest request)
{
  if (errors.hasErrors()) {
    // error handling code goes here.
  }
  ...
}

errors parameter is needed to be placed on the right after the model.

See below for details (Example 17.1):

http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-methods

jsp:

<form:form modelAttribute="articleFormModel" ... >
  ...
  <form:errors path="price" />
</form:form>

message properties file:

typeMismatch.articleFormModel.price=customized error message
Wallachia answered 20/8, 2013 at 5:0 Comment(3)
I'd never think that the order of arguments would matter and that BindingResults had to go right after @ModelAttribute. Thanks for that, now it's working perfectly!Sori
For the records, it works because you used a primitive type. I had the same issue with a java.util.Date attribute and I'm still getting 400 Bad Request.Cockleboat
@Charles Morin form:errors should work for java.util.Date too. Asking another question may help you.Wallachia

© 2022 - 2024 — McMap. All rights reserved.