在控制器方法之前调用Spring mvc验证异常处理程序

在控制器方法之前调用Spring mvc验证异常处理程序,spring,spring-mvc,Spring,Spring Mvc,我有以下代码: public class StudentController extends BaseController { @RequestMapping(value = "/student/edit", method = RequestMethod.POST) public String submitForm(@Valid @ModelAttribute(MODEL_ATTRIBUTE_STUDENT) StudentDTO edited,

我有以下代码:

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);
    }
}
而是执行ExceptionHandler并返回errorpage.html

我没有在视图中使用spring标记(form:form)。我使用的是thymeleaf+boostrap+jquery 编辑: 以下是所要求的例外情况:

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)
StudentTo类有学生需要输入的一系列日期。如下所示

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

下面是如果我从basecontroller的handleAllException()方法抛出一个错误并记录stacktrace会发生什么

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)

您会注意到异常是在
ModelAttributeMethodProcessor#resolveArgument(..)
处引发的。发生在这里

if (binder.getBindingResult().hasErrors()) {
    if (isBindExceptionRequired(binder, parameter)) {
        throw new BindException(binder.getBindingResult());
    }
}
因此,如果解析日期参数时出错(或任何其他错误),并且
isBindExceptionRequest(..)
返回true,那么
BindingResult
将包装在
BindException
中并抛出。那么什么是isBindExceptionRequires(..)

它是这样实现的

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;
}
由于在
@modeldattribute
参数旁边有一个类型为
Errors
BindingResult
是一个子类型)的参数,因此不会抛出
BindException

这意味着您将从某个其他处理程序方法中获得异常,在该方法中,您没有在命令对象参数之后添加
BindingResult
。或者在您的配置中还有一些您没有向我们展示的内容。

工作示例:

@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...
}

尝试:删除@Valid注释。我已经做过很多次了,但从未使用过那个注释,所以问题可能就在那里。有什么例外?打印出它的stacktrace。同时将你的
studentdt发布到
类。我已经尝试删除@Valid注释,但也没有帮助。这是我的第一个猜测:)是不是服务器错误(代码500)?我认为无效字段应该会导致正常的状态200响应,因为没有发生异常,但在这里,解析日期时可能会出现java异常?在完成所有筛选器后,将调用其HttpServlet.service,然后调用FrameworkServlet.service。在stacktrace中没有看到任何其他处理程序。也许我应该抛出一个异常并从basecontroller?@user2023507中的handleAllException()方法打印堆栈跟踪,由
handler
,我指的是您的
@RequestMapping
注释方法。将堆栈跟踪添加到原始问题中。如果其他处理程序/请求映射方法抛出错误,它应该是堆栈跟踪的一部分,不是吗?@user2023507原始异常是在
ModelAttributeMethodProcessor
中抛出的,它是Spring MVC的一个组件,尝试生成
@ModelAttribute
参数并验证它。这就是堆栈跟踪显示的内容。您的处理程序方法从未到达。因此,如何使其到达处理程序方法?:)删除@Valid注释?我试过了,但似乎不起作用。让我再试一次。不,
BindingResult
不关心处理程序方法中的参数数量。它只关心它位于作为模型属性的参数之后(或用
@RequestBody
@Valid
注释)。您是wright,我准备编辑我的初始参数
public String submitForm(@Valid @ModelAttribute(MODEL_ATTRIBUTE_STUDENT) StudentDTO edited,
                                     BindingResult bindingResult, RedirectAttributes attributes) {
@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...
}