【问题标题】:Spring Validation: Null ModelAndView returned to DispatcherServletSpring Validation:Null ModelAndView 返回 DispatcherServlet
【发布时间】:2015-10-31 08:42:53
【问题描述】:

我很确定这个问题以前没有在这里描述过。我也很确定这是我的一个愚蠢的错误。

问题简介:

  • 我创建了一个用户登录表单。
  • 当用户登录信息是 在服务层正确或失败,一切正常
  • 当用户 输入验证失败,我从 spring 收到以下消息: “空 ModelAndView 返回 DispatcherServlet 并带有名称 'AssessmentAdmin':假设 HandlerAdapter 完成请求 处理”
  • 然后我在网页上收到 400 错误:“The request sent by the 客户端在语法上不正确。”

在我看来:

当登录信息正确时,模型不为空。我可以从日志中看到这一点(见下文)。 当用户输入不满足验证要求时,所有的 AOP 调用验证都会进行,但我的控制器方法甚至没有被调用(再次,请参阅日志)。

JSP

登录表格:

<%@ page contentType="text/html; charset=UTF-8"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>

<html>
<head>
<title><spring:message code="label.login" /></title>
<style>
.error {
	color: red;
	font-weight: bold;
}
</style>
</head>
<body>



	<h2 align="center">
		<spring:message code="label.login" />
	</h2>
	<h2 align="center">
		<spring:message code="${loginError}" />
	</h2>
	<form:form method="POST" action="/prototype2/xz/doXzLogin" commandName="userAdminForm">
		<table>
			<tr>
				<td><form:label path="userName" >
						<spring:message code="label.name" />
						<form:errors path="userName" cssClass="error"/>
					</form:label></td>
				<td><form:input path="userName" /></td>
			</tr>
			<tr>
				<td><form:label path="password">
						<spring:message code="label.password" />
						<form:errors path="password" cssClass="error"/>
					</form:label></td>
				<td><form:input path="password" /></td>
			</tr>
			<tr>
				<td><form:label path="pin">
						<spring:message code="label.pin" />
						<form:errors path="pin" cssClass="error"/>
						
					</form:label></td>
				<td><form:input path="pin" /></td>
			</tr>
			<tr>
				<td colspan="2"><input type="submit" value="Submit" /></td>
			</tr>
		</table>
	</form:form>

</body>
</html>

登录成功表单(暂时只是一个测试页面)

<%@ page contentType="text/html; charset=UTF-8" %>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>

<html>
<head>
<title><spring:message code="label.hello"/>:  ${userName}</title>
</head>
<body>


<h3 align="center"><spring:message code="label.hello"/>, ${userName}</h3>
  
  <form:form method="POST" action="/prototype2/performXzLogin" commandName="userAdminForm">
   <table>
    <tr>
        <td><form:label path="userName"><spring:message code="label.name"/></form:label></td>
        <td>${userName}</td>
    </tr>
    <tr>
        <td><form:label path="password"><spring:message code="label.password"/></form:label></td>
        <td>${password}</td>
    </tr>
    <tr>
        <td><form:label path="pin"><spring:message code="label.pin"/></form:label></td>
        <td>${pin}</td>
    </tr>
    
</table>  
</form:form>
   
</body>
</html>

控制器

  package com.prototype2.controller.user;

  import javax.validation.Valid;

  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.stereotype.Controller;
  import org.springframework.ui.Model;
  import org.springframework.validation.BindingResult;
  import org.springframework.web.bind.annotation.ExceptionHandler;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.bind.annotation.RequestMethod;

  import com.prototype2.controller.welcome.SS;
  import com.prototype2.controller.welcome.SpringException;
  import com.prototype2.model.user.UserAdmin;
  import com.prototype2.service.user.AdminUserService;
  import com.prototype2.service.user.BusinessService;

  @Controller
  public class UserControllerImpl
  {

   @Autowired
   private AdminUserService auService;

   @Autowired
   private BusinessService businessService;

   @RequestMapping(value = "/xzlogin", method = RequestMethod.GET)
   public String xzLogin(Model model)
   {

    UserAdmin ua = new UserAdmin();
    model.addAttribute("userAdminForm", ua);
    model.addAttribute("loginError", "label.blank");

    SS.getLogger().debug("xxx: xzLogin done");
    return "xzlogin";

   }

   @RequestMapping(value = "/doXzLogin", method = RequestMethod.POST)
   @ExceptionHandler(
   { SpringException.class })
   public String doXzLogin(@Valid UserAdmin u, Model model, BindingResult bindingResult)
   {

    SS.getLogger().debug("doXzLogin xxx UserAdmin: " + u.toString());
    SS.getLogger().debug("doXzLogin xxx Model size: " + model.toString());
    if (bindingResult.hasErrors())
    {
     model.addAttribute("loginError", "label.loginfailed");
     return "xzlogin";
    }
    SS.getLogger().debug("doing login: listing entries if any...");

    SS.getLogger().debug("User Logging In:   " + u.getUserName());
    try
    {

     model.addAttribute("userAdminForm", u);
     // model.addAttribute("userName", u.getUserName());

     // model.addAttribute("pin", u.getPin());

     // model.addAttribute("password", u.getPassword());

     if (auService.doAdminLogin(u))
     {

      return "xzloginresult";
     } else
     {
      model.addAttribute("loginError", "label.loginfailed");

      return "xzlogin";

     }
    } catch (Exception e)
    {
     SS.getLogger().error(e.getMessage(), e);
     throw new SpringException(e.getMessage() + "Login Failed: Incorrect username, password, or pin");
    }

   }

  }

ApplicationContext

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="  
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
   http://www.springframework.org/schema/aop 
   http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-3.1.xsd 
   http://www.springframework.org/schema/tx
   http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 
   http://www.springframework.org/schema/mvc
   http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

	<!-- i18n -->
	<bean id="messageSource"
		class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
		<property name="basename" value="classpath:messages" />
		<property name="defaultEncoding" value="UTF-8" />
	</bean>

	<bean id="localeChangeInterceptor"
		class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
		<property name="paramName" value="lang" />
	</bean>

	<bean id="localeResolver"
		class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
		<property name="defaultLocale" value="en" />
	</bean>

	<bean id="handlerMapping"
		class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
		<property name="interceptors">
			<ref bean="localeChangeInterceptor" />
		</property>
	</bean>

	<!-- Hibernate -->
	                        ...
	
	<!--  validation -->
	<mvc:annotation-driven validator="validator" />
	
	<bean id="validator"
    class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
</beans>

成功登录的日志(没有错误,没有问题。模型不为空):

>>******Controller Log: Successful login******





>>
>>
>>[DEBUG] "jake.app" 2015-08-07 11:47:00,731: Locale  en
>>
>>[DEBUG] "jake.app" 2015-08-07 11:47:02,845: xxx: xzLogin done
>>
>>[DEBUG] "jake.app" 2015-08-07 11:47:14,305: doXzLogin xxx UserAdmin: UserAdmin [userName=jake0, password=1234, pin=1234, id=null]
>>
>>[DEBUG] "jake.app" 2015-08-07 11:47:14,306: doXzLogin xxx Model size: {userAdmin=UserAdmin [userName=jake0, password=1234, pin=1234, id=null], org.springframework.validation.BindingResult.userAdmin=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
>>
>>[DEBUG] "jake.app" 2015-08-07 11:47:14,311: doing login: listing entries if any...
>>
>>[DEBUG] "jake.app" 2015-08-07 11:47:14,313: User Logging In:   jake0
>>
>>[DEBUG] "jake.app" 2015-08-07 11:47:14,503: in ua service
>>
>>[DEBUG] "jake.app" 2015-08-07 11:47:14,504: logging in through dao :jake0 1234 1234
>>
>>[DEBUG] "jake.app" 2015-08-07 11:47:14,972: User Admin is UserAdmin [userName=jake0, password=1234, pin=1234, id=206]
>>
>>[DEBUG] "jake.app" 2015-08-07 11:47:14,974: User Admin is UserAdmin [userName=jake0, password=1234, pin=1234, id=206]
>>
>>



******Root Log (i.e., Spring Log): Successful login******
>>
>>[DEBUG] 2015-08-07 11:47:14,297: org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:861) DispatcherServlet with name 'AssessmentAdmin' processing POST request for [/prototype2/xz/doXzLogin]
>>
    (cut for sake of brevity)
>>
>>[DEBUG] 2015-08-07 11:47:15,002: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1616) Invoking afterPropertiesSet() on bean with name 'xzloginresult'
>>
>>[DEBUG] 2015-08-07 11:47:15,003: org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:248) Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
>>
>>[DEBUG] 2015-08-07 11:47:15,004: org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1241) Rendering view [org.springframework.web.servlet.view.InternalResourceView: name 'xzloginresult'; URL [/WEB-INF/jsp/xzloginresult.jsp]] in DispatcherServlet with name 'AssessmentAdmin'
>>
>>[DEBUG] 2015-08-07 11:47:15,005: org.springframework.web.servlet.view.AbstractView.exposeModelAsRequestAttributes(AbstractView.java:432) Added model object 'userAdmin' of type [jake.prototype2.model.user.UserAdmin] to request in view with name 'xzloginresult'
>>
>>[DEBUG] 2015-08-07 11:47:15,006: org.springframework.web.servlet.view.AbstractView.exposeModelAsRequestAttributes(AbstractView.java:432) Added model object 'org.springframework.validation.BindingResult.userAdmin' of type [org.springframework.validation.BeanPropertyBindingResult] to request in view with name 'xzloginresult'
>>
>>[DEBUG] 2015-08-07 11:47:15,008: org.springframework.web.servlet.view.AbstractView.exposeModelAsRequestAttributes(AbstractView.java:432) Added model object 'userAdminForm' of type [jake.prototype2.model.user.UserAdmin] to request in view with name 'xzloginresult'
>>
>>[DEBUG] 2015-08-07 11:47:15,010: org.springframework.web.servlet.view.AbstractView.exposeModelAsRequestAttributes(AbstractView.java:432) Added model object 'org.springframework.validation.BindingResult.userAdminForm' of type [org.springframework.validation.BeanPropertyBindingResult] to request in view with name 'xzloginresult'
>>
>>[DEBUG] 2015-08-07 11:47:15,012: org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:166) Forwarding to resource [/WEB-INF/jsp/xzloginresult.jsp] in InternalResourceView 'xzloginresult'
>>
>>[DEBUG] 2015-08-07 11:47:15,046: org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:996) Successfully completed request
>>

验证错误的登录日志(模型为空):**

>>******Controller Log: failed login******
>>
>>
>>[DEBUG] "jake.app" 2015-08-07 11:39:34,781: xxx: xzLogin done
>>
>>
>>
>>
>>


>>Root Log (i.e., Spring Log): failed login******
>>
>>
>>
>>[DEBUG] 2015-08-07 11:39:42,741: org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:248) Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
>>
>>[DEBUG] 2015-08-07 11:39:42,742: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:478) Finished creating instance of bean 'org.hibernate.validator.internal.constraintvalidators.NotNullValidator'
>>
>>[DEBUG] 2015-08-07 11:39:42,749: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450) Creating instance of bean 'org.hibernate.validator.internal.constraintvalidators.MinValidatorForNumber'
>>
>>[DEBUG] 2015-08-07 11:39:42,755: org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:248) Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
>>
>>[DEBUG] 2015-08-07 11:39:42,756: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:478) Finished creating instance of bean 'org.hibernate.validator.internal.constraintvalidators.MinValidatorForNumber'
>>
>>[DEBUG] 2015-08-07 11:39:42,802: org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134) Resolving exception from handler [public java.lang.String jake.prototype2.controller.user.UserControllerImpl.doXzLogin(jake.prototype2.model.user.UserAdmin,org.springframework.ui.Model,org.springframework.validation.BindingResult)]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
>>Field error in object 'userAdmin' on field 'pin': rejected value [0]; codes [Min.userAdmin.pin,Min.pin,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userAdmin.pin,pin]; arguments []; default message [pin],1000]; default message [must be greater than or equal to 1000]
>>
>>[DEBUG] 2015-08-07 11:39:42,807: org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134) Resolving exception from handler [public java.lang.String jake.prototype2.controller.user.UserControllerImpl.doXzLogin(jake.prototype2.model.user.UserAdmin,org.springframework.ui.Model,org.springframework.validation.BindingResult)]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
>>Field error in object 'userAdmin' on field 'pin': rejected value [0]; codes [Min.userAdmin.pin,Min.pin,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userAdmin.pin,pin]; arguments []; default message [pin],1000]; default message [must be greater than or equal to 1000]
>>
>>[DEBUG] 2015-08-07 11:39:42,809: org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134) Resolving exception from handler [public java.lang.String jake.prototype2.controller.user.UserControllerImpl.doXzLogin(jake.prototype2.model.user.UserAdmin,org.springframework.ui.Model,org.springframework.validation.BindingResult)]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
>>Field error in object 'userAdmin' on field 'pin': rejected value [0]; codes [Min.userAdmin.pin,Min.pin,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userAdmin.pin,pin]; arguments []; default message [pin],1000]; default message [must be greater than or equal to 1000]
>>
>>[DEBUG] 2015-08-07 11:39:42,812: org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134) Resolving exception from handler [public java.lang.String jake.prototype2.controller.user.UserControllerImpl.doXzLogin(jake.prototype2.model.user.UserAdmin,org.springframework.ui.Model,org.springframework.validation.BindingResult)]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
>>Field error in object 'userAdmin' on field 'pin': rejected value [0]; codes [Min.userAdmin.pin,Min.pin,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userAdmin.pin,pin]; arguments []; default message [pin],1000]; default message [must be greater than or equal to 1000]
>>
>>[DEBUG] 2015-08-07 11:39:42,815: org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134) Resolving exception from handler [public java.lang.String jake.prototype2.controller.user.UserControllerImpl.doXzLogin(jake.prototype2.model.user.UserAdmin,org.springframework.ui.Model,org.springframework.validation.BindingResult)]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
>>Field error in object 'userAdmin' on field 'pin': rejected value [0]; codes [Min.userAdmin.pin,Min.pin,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userAdmin.pin,pin]; arguments []; default message [pin],1000]; default message [must be greater than or equal to 1000]
>>
>>[DEBUG] 2015-08-07 11:39:42,817: org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1034) Null ModelAndView returned to DispatcherServlet with name 'AssessmentAdmin': assuming HandlerAdapter completed request handling
>>
>>[DEBUG] 2015-08-07 11:39:42,818: org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:996) Successfully completed request
>>
>>
>>******HTTP 400 message ******
>>The request sent by the client was syntactically incorrect.
>>
>>
>>
>>

【问题讨论】:

  • @Mudassar,感谢您的关注。我没有编写任何事务代码,因为我依赖声明性事务。我错过了什么吗?
  • 尝试删除后控制器方法中的模型参数,
  • 登录失败检查也应该是验证的一部分,应该由 BindingResults 处理
  • 进步!我现在了解我的方法。我能再麻烦你一点吗?我现在如何访问模型以便添加 model.addAttribute("loginError", "label.loginfailed");

标签: spring validation model-view-controller model


【解决方案1】:

 密码检查应该是通过 BindingResult 进行验证的一部分。因此,在 post 的控制器方法中,我们手动将 JSR-303 错误添加到 BindingResult。

if (bindingResult.hasErrors()){
       return "xzlogin"; 
    }


model.addAttribute("userAdminForm", u); 

if (!auService.doAdminLogin(u)){
bindingResult.addError(new FieldError("userAdminForm","password","Invalid password "));
return "xzlogin"; 
                }

这允许 Spring 通过 FieldError 在视图中显示它们

【讨论】:

  • 绝对是的!谢谢一百万。
  • 但是,在不占用您太多时间的情况下,您能否解释一下为什么模型在验证通过时不为空,而在验证失败时为空?还有为什么这会导致调用控制器方法 doXz​​Login 失败。我喜欢 AOP,但有时它非常不透明
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-01
  • 1970-01-01
  • 2020-05-24
  • 1970-01-01
  • 2021-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多