Friday, May 27, 2011

Spring Validation is still pretty dumb

HOW IT USED TO BE

One of the annoying things about Spring MVC 2.5 was the validation. Even Spring themselves have acknowledged it:

"In previous versions it was up to the developer to manually invoke validation logic"

HOW IT IS NOW

When Spring 3 rolled around, I was looking forward to some clever validator improvements. And Spring delivered!

You no longer have to manually invoke validators, but you can use the JSR-303 @Valid annotation to AUTOMAGICALLY invoke validation on the bean in your controller. Check it out below:

 @Controller  
 public class MyController {  
   @InitBinder  
   protected void initBinder(WebDataBinder binder) {  
     binder.setValidator(new FooValidator());  
   }  
   @RequestMapping("/foo", method=RequestMethod.POST)  
   public void processFoo(@Valid Foo foo) { ... }  
 }  

Awesome sauce!

WHY IT IS STILL DUMB

Yeah that's cool and all, but that's pretty much all they've done in the way of magic. When it comes to CONFIGURING validators (which is one place we could really use some magic), Spring Validation is still pretty stoopid.

You basically have two options - manually set the validator in your initBinder() (like you can see in the example above), or call setValidator (Validator) on the global WebBindingInitializer. Which looks ike this in the applicationContext.xml:

 <mvc:annotation-driven validator="globalValidator"/>  

This is pretty dumb too - having a single global validator for all models probably isn't very useful.

WHY IT IS NOT NORMALLY A PROBLEM

Now all of this isn't so bad when using Spring Validation with Spring MVC. Normally We have one Model per Controller, which is configured with one Validator (and incidentally one View - which is why it is called (yes you guessed it) MVC ).

So configuring a validator for a controller isn't too bad - we're already configuring the model and view for the contrller, so one more thing is not a problem.

WHEN IT IS A PROBLEM

It becomes a problem when you want to use it OUTSIDE of the MVC framework. When you don't have a controller to configure your validator and model to - how on earth do you wire this stuff up?

Suppose I have an arbitrary model that I want validated with Spring's framework - how do I do it? I don't need any of Spring's Binding facilities. And nothing magical like this seems to exist:

 @Autowired MagicalSpringValidationMonkey magicalSpringValidationMonkey;  
 public void myService(MyModel myModel){  
   Errors errors = magicalSpringValidationMonkey.validate(myModel);  
 }  

I would expect my Magical Spring validator to do three things:
1. Run any JSR-303 Bean Validation configured for the model
2. Find and run the appropriately configured Spring Validator for this model
3. Return me an errors instance containing all of those errors.


WHY IT SHOULDN'T BE A PROBLEM

JSR-303 Validation is configured on the model itself, so Spring should be able to figure out how to run that validation.

Spring Validators (my custom validation classes that implement Spring's Validator interface) are already configured to show which models they support, by means of the supports(Class clazz) method which must be implemented.

HMMM COME TO THINK OF IT

Actually now that I think about it, you know what? After writing this long rant, I figured out that it probably wouldn't be too hard for me to write the MagicalSpringValidationMonkey myself.

But you know what else? I'm annoyed that this isn't already there, and that I had to do so much investigation to find out how it works, so I'm not gonna do it. Plus I'm too lazy. And Margaret River is calling me.

I hope you enjoyed this rant.

The end.

2 comments:

Keith Donald said...

I'm not sure you're getting how the JSR 303 validation works. In Spring MVC, JSR 303 is wrapped in a shared Spring Validator. You can use @Valid to have it invoked as part of a @Controller method argument binding, or you can inject it and invoke it programmatically. In both these cases, it will apply the constraints specific to the model you're validating.

Keith

Daniel Alexiuc said...

Hi Keith,
Thanks for taking an interest in my post. My main point wasn't about Spring MVC or JSR-303, I am interested in using Spring Validators outside of Spring MVC.

In MVC it sort of makes sense to be forced to explicitly specify your model and validator inside the controller, because they are all kind of related.

But outside of MVC, why can't I just ask Spring to validate an arbitrary model? I have already configured the validator with the models that it supports(), so why am I forced to programmatically inject and call the validator myself? This is the part of Spring Validator that could use some more intelligence - there is no reason I should have to configure this stuff twice.