Spring MVC如何解析和验证处理程序方法参数?

时间:2015-10-10 11:38:40

标签: java spring spring-mvc hibernate-validator spring-validator

我是Spring MVC的新手,我已经导入了一个与服务器端验证相关的教程项目,我对它究竟是如何工作有一些疑问。

所以我有一个名为 login.jsp 的登录页面,其中包含此登录表单:

<form:form action="${pageContext.request.contextPath}/login" commandName="user" method="post">

    <table>

        <tr>
            <td><label>Enter Username : </label></td>
            <td><form:input type="text" path="username" name="username" />
                <br> <form:errors path="username" style="color:red;"></form:errors>
            </td>
        </tr>

        <tr>
            <td><label>Enter Password : </label></td>
            <td><form:input type="password" path="password" name="password" />
                <br> <form:errors path="password" style="color:red;"></form:errors>
            </td>
        </tr>

        <tr>
            <td>&nbsp</td>
            <td align="center"><input type="submit" value="Login" /></td>
        </tr>

    </table>

</form:form>

我认为使用从模型中检索到的 commandName =“user”属性指定的对象(如果我做错了断言,请将其核心)存储用户插入的用户名和密码。

commandName =“user”是此用户类的一个实例:

import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotBlank;

public class User {

    @NotBlank(message="Username can not be blank")
    private String username;

    @Size(min=6,message="Password must be atleast 6 characters long")
    private String password;

    private String gender;
    private String vehicle;
    private String country;
    private String image;

    ...............................................
    ...............................................
    GETTER AND SETTER METHODS
    ...............................................
    ...............................................
}

因此,您可以看到用户名密码上声明了 @NotBlank @Size 验证注释strong> fields。

这是第一个疑问:与2使用的库 javax.validation org.hibernate.validator 的区别究竟是什么?

教程中为什么同时使用?我可以只使用hibernate验证器库做同样的事情吗? (我认为我可以使用Hibernate验证器指定字符串的有效长度,不是吗?)

然后,当提交登录表单时,它会生成HttpRequest并将 / login 资源生成,该资源由声明为控制器类的此方法处理:

@RequestMapping(value="/login" , method=RequestMethod.POST)
public String do_login(HttpServletRequest req , Model md , HttpSession session , @Valid User user, BindingResult br)
{
    try
    {
        //System.out.println(br.getAllErrors().size());

        String username = req.getParameter("username");
        String password = req.getParameter("password");

        System.out.println("Username and pasword are : "+username +"  "+ password);
        if(br.getAllErrors().size() > 0){
            System.out.println("Server side validation takes place....");
        }
        else{
        Login_Model lm = new Login_Model();
        String message = lm.do_login_process(username, password);

        if(message.equals("login success"))
        {
            session.setAttribute("username", username);
            return "redirect:/myprofile";
        }
        else
        {
            md.addAttribute("error_msg", message);
        }
        }
        return "login";
    }
    catch(Exception e)
    {
        return "login";
    }
}

好的,现在我对这种方法有以下疑问:

1)将此对象作为输入参数: @Valid用户用户。 谁传递给它?我认为它可能取决于我指定 commandName =“user”的形式,因此Spring会自动执行此操作。这是对的吗?

2)根据我的理解, @Valid 注释会自动调用验证过程。怎么会发生?与Spring提供的 AOP 功能有关吗?或者是什么?为什么此 @Valid 注释仅与 javax.validation 库相关,而不与Hibernate验证器相关(因此 @Valid 注释也会验证用Hibernate验证器注释注释的字段?为什么?)

3)根据我的理解,如果用户在登录表单中插入了错误的值,我可以通过 BindingResult br 输入参数获取此错误。使用调试器,我可以看到该对象包含由用户模型对象中定义的注释定义的错误消息。究竟是如何运作的?

TNX

3 个答案:

答案 0 :(得分:5)

  

这是第一个疑问:2个使用过的库javax.validationorg.hibernate.validator之间究竟有什么区别?

javax.validation来自JSR-303 API。您可以在this Maven dependency中查看API类。

Hibernate validator是JSR 303的实现之一(实际上是参考实现),因此它实现了所有API,但添加了自己的扩展,例如您提到的@NotBlank注释。 JSR 303的其他实现例如是Apache BVal

  

它将此对象作为输入参数:@Valid User user。谁将它传递给处理程序方法?

Spring MVC中处理程序方法的值由接口HandlerMethodArgumentResolver的实现提供。在您的情况下,将调用以解析User参数的实现可能是ServletModelAttributeMethodProcessor。您可以查看这些类的源代码和JavaDoc,以了解它们如何在内部工作。

  

@Valid注释将自动调用验证过程。这是怎么回事?它使用AOP吗?为什么这个@Valid注释只与javax.validation库相关,而不与Hibernate验证器相关(所以这个@Valid注释也验证了用Hibernate验证器注释注释的字段?)

验证过程由ModelAttributeMethodProcessor调用,这是前面提到的类ServletModelAttributeMethodProcessor继承的。它包含以下方法:

protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
    Annotation[] annotations = parameter.getParameterAnnotations();
    for (Annotation ann : annotations) {
        if (ann.annotationType().getSimpleName().startsWith("Valid")) {
            Object hints = AnnotationUtils.getValue(ann);
            binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
            break;
        }
    }
}

如果仔细观察,您会看到以下表达式的条件:

ann.annotationType().getSimpleName().startsWith("Valid")

这意味着如果参数具有以Valid开头的任何注释,则Spring将调用验证。它可能是JSR 303的@Valid或Spring的@Validated,它支持验证组。它甚至可以是您的自定义注释,只要其名称以Valid开头。

  

根据我的理解,如果用户在登录表单中插入了错误的值,我可以从BindingResult处理程序参数中获取此错误。使用调试器,我可以看到该对象包含由定义到User模型对象中的注释定义的错误消息。究竟是如何运作的?

让我们回到ModelAttributeMethodProcessor课程。其resolveArgument方法中包含以下代码:

WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);

这将创建WebDataBinder的实例,在前面提到的validateIfApplicable方法中调用验证。验证本身填充BindingResult,然后通过ErrorsMethodArgumentResolver类提供给控制器处理程序方法,该类再次实现HandlerMethodArgumentResolver

TLDR :您在此问题中提出的许多问题都可以追溯到HandlerMethodArgumentResolver的各种实现。我建议通过这些课程并使用调试器逐步完成它们,以便更好地理解它们。

答案 1 :(得分:2)

  

与2使用的库有什么区别?   javax.validation和org.hibernate.validator?

Bean Validation是一个Java 规范,它允许您通过注释表达对象模型的约束,允许您以可扩展的方式编写自定义约束。 这只是一个完全没有实施的规范 。 Hibernate Validator是此规范的实现。 Hibernate Validator完全实现了Bean Validation,它还具有hibernate独有的一些功能。简而言之,javax.validation是规范部分,org.hibernate.validator是hibernate的独特功能。

  

我是否可以仅使用hibernate验证器库执行相同的操作? (一世   我认为我可以使用。指定字符串的有效长度   Hibernate验证器,或不?)

是的,你可以但最好坚持使用javax.validation命名空间,并且只使用供应商特定命名空间的独特功能,例如org.hibernate.validator。这种方法的好处是你可以将Hibernate Validator的Bean Validation实现切换到其他 ,只需更改

  

教程中为什么同时使用两者?

因为{Val(1)}约束在Bean Validation规范中不可用,并且仅由Hibernate Validator提供。

  

将此对象作为输入参数:@Valid User user。谁传递给   它?我认为这可能取决于我的形式   指定的commandName =“user”所以Spring自动执行它。是吗   正确的吗?

可能你有@NotBlank url的GET控制器,它返回一个空的login对象,后者又与User等弹簧标签绑定。当用户填写表单并提交表单时,spring会收集这些标记值并创建<form:input type="text" path="username" name="username" />的实例并将其传递给控制器​​。

  

为什么这个@Valid注释只与javax.validation有关   库而不是Hibernate验证器(所以@Valid   annotation还验证使用Hibernate验证器注释的字段   注解?为什么呢?)

已经解释过,Spec / Impl故事!

答案 2 :(得分:2)

  1. 正确,称为属性绑定
  2. 如果类路径上有任何JSR 303验证器框架,Spring 3+支持通过JSR303注释进行@Valid Bean验证。 Hibernate Validator是JSR 303参考实现之一

  3. 任何约束违规都会在BindingResult对象中公开为错误,因此您可以像控制方法一样检查控制器方法中的违规行为

    if(br.getAllErrors()。size()&gt; 0){         System.out.println(&#34;服务器端验证发生....&#34;);     }

  4. 总结,Spring MVC将使用来自使用Spring的表单标记的JSP表单的输入验证@Valid之后binding its properties注释注释的模型对象。任何约束违规都将作为BindingResult对象中的错误公开 here is a good post