Announcement Announcement Module
Collapse
No announcement yet.
@Valid, binder.setValidator and other fun Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • @Valid, binder.setValidator and other fun

    ok, we are trying out spring 3 rc1 and have run into some interesting behavior.

    The first is a bit of a warning to people using binder.setValidator, if you are returning some object of different types into the page make sure you use

    @InitBinder("ourBackingObject") rather then
    @InitBinder for setting your Validator...

    The reason is that InitBinder is called for every object being placed into a page.
    While that isn't a bad thing, it checks the validators supports method when the validator is set... throwing an exception if it is of the wrong type.

    Which can be bad, because if you have 2 objects being placed on the page, one being your backing object, and one being something else, it will fail when it calls setValidator... since the InitBinders target is not what the validator expects.

    an example.

    if you had a backing Object of type Foo you would expect to be able to..
    Code:
        @InitBinder
        void initBinder(WebDataBinder binder) {
            binder.setValidator(new FooValidator());
        }
    and you can... but if you put a Bar object into your ModelAndView then InitBinder will be called with a target of your Bar object.

    and setValidator will fail.

    however if you have
    Code:
        @InitBinder("foo")
        void initBinder(WebDataBinder binder) {
            binder.setValidator(new FooValidator());
        }
    then all is good with the world, since it will only be called for the "foo" object.



    ok, the part I can't get working with the validator...

    Code:
    @InitBinder("foo")
    void initBinder(WebDataBinder binder) {
        binder.setValidator(new FooValidator());
    }
    
    @RequestMapping(method = POST)
    ModelAndView create(@Valid @ModelAttribute Foo foo, BindingResult binder) {
           // whatever it won't get then far....
        }
    when FooValidator fails, (because one of foo's field is invalid) it throws its toys out of the cot...

    Code:
    org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
    Field error in object 'foo' on field 'code': rejected value [a]; codes [code.isTooShort.foo.code,code.isTooShort.code,code.isTooShort.java.lang.String,code.isTooShort]; arguments []; default message [null]
    	at org.springframework.web.bind.support.WebRequestDataBinder.closeNoCatch(WebRequestDataBinder.java:116)
    	at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.doBind(HandlerMethodInvoker.java:686)
    	at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:288)
    	at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:156)
    	at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:378)
    	at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:366)
    	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:781)
    	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:726)
    	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:636)
    	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:556)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
    so the @Valid annotation is working, Foo is being Validated, but...
    The validation is failing, but... it crashes out rather then the method being called....


    hell even having
    Code:
        @ExceptionHandler(BindException.class) 
        String failedValidate() {
            return "view";
        }
    won't save it.

    so...

    Does anyone have a demo with @Valid and setValidator working so we can see what we are doing wrong? Oh as a note, we can not annotate the Entity, so....

    --- Blair

  • #2
    You need to return to the form when there is any validation errors:
    Code:
    @RequestMapping(method = POST)
    ModelAndView create(@Valid @ModelAttribute Foo foo, BindingResult binder) {
    
    	if (binder.hasErrors()) {
    	    //	return to the form
                ...
    	}
            ...
    }
    I haven't figured out how to customize the validation error message so that it can be i18n.

    Comment


    • #3
      found the problem...

      well, yes we do... however, it we were not getting that far...

      Code:
      @RequestMapping(method = POST)
      ModelAndView create(@Valid @ModelAttribute Foo foo, BindingResult binder) {
      
      	if (binder.hasErrors()) {
      	    //	return to the form
                  ...
      	}
              ...
      }

      Code:
      ModelAndView create(@Valid @ModelAttribute Foo foo, BindingResult binder)
      needs to be
      Code:
      ModelAndView create(@Valid Foo foo, BindingResult binder)
      or it doesn't work... that was the solution we needed

      Comment


      • #4
        Oy vay, this is all so confusing. I'm just getting into framework-managed validation (vs. doing everything by hand), and there's just so much stuff and very little documentation. It seems, from what I've read so far both in the official documentation and from others' posts, that using framework-managed validation is just as much work as manual validation. Except that at least manual validation is closer to the controller code (read: inline), whereas the former is hidden away somewhere in a gazillion other classes. It also seems that you can only have one validator for your entire application.
        Out of the dozen or more classes required to implement validation, I'm confounded about which to use and which ones conflict and do not conflict with each other, and when to use annotations and, among the available annotations, when to use @ModelAttribute and @Valid and plain model objects as controller method parameters (what is a model attribute anyway? What is a model doing as a parameter anyway??). I think at this point most people are better off writing their own validation frameworks until the industry can sort this out. I myself am quite ready to rip the proverbial hair out of the non-proverbial head.

        Michael

        Comment


        • #5
          I have also tried:

          public void second(@Valid Absence abs, BindingResult result){

          and
          public void second(@Valid @ModelAttribute Absence abs, BindingResult result){


          but everytime I got the error "annotation type not applicable for this kind of declaration". What's this? Someone please assist me!

          Comment


          • #6
            You have to use the Bean Validation API (javax.validation.Valid)

            take a look at this http://stackoverflow.com/questions/1...th-spring-3-x/

            Comment


            • #7
              In my case the order of the arguments also were important:

              throws Error: public void handlePostRequest(@Valid Foo foo, Model model, BindingResult result)

              Correct: public void handlePostRequest(@Valid Foo foo, BindingResult result, Model model)

              From the 3.0 reference:
              >>>>>>
              org.springframework.validation.Errors / org.springframework.validation.BindingResult validation results for a preceding command or form object (the immediately preceding method argument).
              >>>>>>>

              Look here:
              http://forum.springsource.org/showthread.php?t=83258

              Comment

              Working...
              X