I’ve been happily using XML-free Spring with Web MVC, right until the moment when I wanted to plug in JSR-303 validation.
Failure
I imported validation-api
and hibernate-validator
to my project. I annotated code for my command:
public class SpendingCommand { @Size(min=3) private String category; // ... }
… and controller:
@Controller public class SpendingEditionController { @RequestMapping(value = "/spending_insert", method = RequestMethod.POST) public String addSpending(@Valid SpendingCommand spending, BindingResult result, ModelMap model) { return "my_view"; } // ... }
I plugged it in to form:
#springBind("command.$field") <label for="$field" class="control-label">${label}:</label> <div class="controls"> <input type="text" name="${status.expression}" value="$!{status.value}" /> $!{status.errorMessage} </div>
… and nothing happened.
I looked for errors in BindingResult
in my controller, and nothing was there. Clearly validation was not working at all.
Almost There: @Valid Working
I read a ton of tutorials, and they did not mention any specific black magic. After a long while of doc reading, random trying and debugging, I found this StackOverflow answer. Skaffman said that <mvc:annotation-driven />
was “rather pointless” so “don’t bother”. Luckily I read comments to that answer as well and discovered that this is actually crucial for all the new goodies in Spring Web MVC, including conversions and validation.
I added annotation equivalent of mvc:annotation-driven
to my view configuration:
@Configuration @ComponentScan(basePackages = "pl.squirrel.money.web") @EnableWebMvc public class ViewConfig
When I tested my code again, I did see errors in BindingResult
in my controller, so finally validation was working. Unfortunately, the web page still did not show the message. Do you know why?
Bindings and Naming Conventions
It took me even longer to figure this one out. I even began to suspect my custom view for Velocity Tools & Tiles.
Finally in debug I noticed I had my command
bound twice in page context: as command
and as spendingCommand
. I had two bindings for BindingResult
as well, but with two different instances! One was org.springframework.validation.BindingResult.command
, with zero errors, and another was org.springframework.validation.BindingResult.spendingCommand
, containing all errors as expected.
In a word, mess. To clean this up, I had to explicitly name my command like this:
@RequestMapping(value = "/spending_insert", method = RequestMethod.POST) public String addSpending(@ModelAttribute("command") @Valid SpendingCommand spending, BindingResult result, ModelMap model) { return "my_view"; }
Now I only have one instance of everything, and everything is working as expected. And they lived happily ever after.
Quirks
In the end, I find it interesting (in a bad sense) that it works like this. I think it’s a bug that the same command is bound under two different names, but it’s quite the opposite for BindingResult
.
To test it, I attempted to edit this SpendingCommand
in controller by overwriting value of a field. At this point I knew what would happen: My web page showed overwritten value in form (because Spring was still able to match the command with different name), but no validation errors (because there are two different instances of BindingResult
.