9

I have following code :

public class StudentController extends BaseController {

@RequestMapping(value = "/student/edit", method = RequestMethod.POST)
    public String submitForm(@Valid @ModelAttribute(MODEL_ATTRIBUTE_STUDENT) StudentDTO edited,
                                         BindingResult bindingResult, RedirectAttributes attributes) {
        if (bindingResult.hasErrors()) {
            return STUDENT_EDIT_FORM_VIEW;
        }

        //some logic...
    }

@InitBinder
public void initBinder(WebDataBinder binder) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
    dateFormat.setLenient(false);

    // true passed to CustomDateEditor constructor means convert empty String to null
    binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
}

public class BaseController {
    @ExceptionHandler(Exception.class)
    protected ModelAndView handleAllException(Exception ex) {
        Long errorId = System.currentTimeMillis() + RandomInteger.generate(1, Integer.MAX_VALUE);

        //do something...

        Map<String, String> parameters = new HashMap<>();
        parameters.put("flash_message", "Things blew up...!");

        return new ModelAndView("/errorpage", parameters);
    }
}

Now, when there is a binding error (example in a date field user entered Feb 40th, 2013), then I expected the following block to be executed:

if (bindingResult.hasErrors()) {
    return STUDENT_EDIT_FORM_VIEW;
}

but instead the ExceptionHandler is executed and errorpage.html is returned..!

I am not using spring tags (form:form) in the view. I am using thymeleaf + boostrap + jquery EDIT: Here is the exception as requested -

org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'student' on field 'responseDate': rejected value [02/30/2014]; codes [typeMismatch.student.responseDate,typeMismatch.responseDate,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.responseDate,responseDate]; arguments []; default message [responseDate]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'responseDate'; nested exception is java.lang.IllegalArgumentException: Could not parse date: Unparseable date: "02/30/2014"]
    at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:110)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:123)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)

StudentDTO class has bunch of dates that student needs to input. Looks like the following

public class StudentDTO {
private Long id;
private String state;
private String region;
private Date responseDate;
private Date stayLiftedDate;
...
...
//Then all getters and setters.
}

EDIT2:

Here is what happens if I throw an error and log stacktrace from handleAllException() method of basecontroller

java.lang.Exception: Sample Error
    at com.mycompany.controller.BaseController.handleAllException(BaseController.java:103)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.doResolveHandlerMethodException(ExceptionHandlerExceptionResolver.java:321)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver.doResolveException(AbstractHandlerMethodExceptionResolver.java:60)
    at org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:136)
    at org.springframework.web.servlet.handler.HandlerExceptionResolverComposite.resolveException(HandlerExceptionResolverComposite.java:73)
    at org.springframework.web.servlet.DispatcherServlet.processHandlerException(DispatcherServlet.java:1148)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:985)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:939)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at com.mycompany.security.CookieAuth.doFilter(CookieAuth.java:50)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:409)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1044)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
5
  • Something to try: remove the @Valid annotation. I have done this many times, but never used that annotation, so maybe the problem lies there. Commented Feb 25, 2014 at 1:51
  • 1
    What's the exception? Print out its stacktrace. Commented Feb 25, 2014 at 2:01
  • 2
    Also post your StudentDTO class. Commented Feb 25, 2014 at 2:10
  • I have already tried removing the @Valid annotation, but that didn't help either. This was my first guess :) Commented Feb 25, 2014 at 19:00
  • Isn't it server error (code 500)? I think that invalid field should result normal status 200 response, because no exception occured, but here maybe you have java exception while parsing Date? Commented Feb 25, 2014 at 19:27

2 Answers 2

11

You will notice the exception is thrown at ModelAttributeMethodProcessor#resolveArgument(..). It happens here

if (binder.getBindingResult().hasErrors()) {
    if (isBindExceptionRequired(binder, parameter)) {
        throw new BindException(binder.getBindingResult());
    }
}

So if there was an error parsing the date parameter (or any other error) and isBindExceptionRequest(..) returns true, the BindingResult is wrapped in a BindException and thrown. So what is isBindExceptionRequires(..)?

It's implemented as such

protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
    int i = parameter.getParameterIndex();
    Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
    boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));

    return !hasBindingResult;
}

and as the Javadoc says

Returns: true if the next method argument is not of type Errors.

In other words, if your handler method is declared like so

public String submitForm(@Valid @ModelAttribute(MODEL_ATTRIBUTE_STUDENT) StudentDTO edited,
                                     BindingResult bindingResult, RedirectAttributes attributes) {

a BindException will not be thrown since there is a parameter of type Errors (BindingResult is a sub type) next to the @ModelAttribute parameter.

This means that you are getting the exception from some other handler method where you don't have the BindingResult following a command object parameter. Or there is something else in your configuration which you aren't showing us.

Sign up to request clarification or add additional context in comments.

7 Comments

After all the filters are done, its HttpServlet.service and then FrameworkServlet.service that get invoked. Haven't seen any other handler in the stacktrace. Maybe I should throw an exception and print stack trace from handleAllException() method in basecontroller?
@user2023507 By handler, I meant your @RequestMapping annotated method.
added the stacktrace to original question. If some other handler/requestmapping method throws the error, it should be part of stack trace isnt it?
@user2023507 The original exception was thrown in ModelAttributeMethodProcessor which is a component of Spring MVC that tries to generate your @ModelAttribute argument and validate it. That's what the stack trace shows. Your handler method is never reached.
So, how can I make it reach the handler method? :) Remove @Valid annotation? I tried that, but, that didn't seem to work. Let me try again.
|
0

working sample:

@RequestMapping(value = "/student/edit", method = RequestMethod.POST)
public String submitForm(@Valid @ModelAttribute(MODEL_ATTRIBUTE_STUDENT)  
StudentDTO edited, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return STUDENT_EDIT_FORM_VIEW;
    }

    //some logic...
}

2 Comments

No, BindingResult does not care about the number of parameters in the handler method. It only cares that it comes after a parameter that is a model attribute (or annotated with @RequestBody and @Valid).
you are wright, i'm goning to edit my initial coment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.