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

gdx19jrr  于 2023-02-07  发布在  Spring
关注(0)|答案(3)|浏览(105)

我是SpringMVC的新手,我已经导入了一个与服务器端验证相关的教程项目,我对它到底是如何工作的有些怀疑。
因此,我有这个名为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”属性指定的对象(如果我错了请纠正我)来存储用户插入的用户名和密码。
命令名称=“用户”是此用户
类的示例:

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

因此您可以看到,在usernamepassword字段中声明了**@NotBlank@Size验证注解。
这里是第一个疑问:与两个常用的库
javax.validationorg.hibernate.validator的区别到底是什么?
为什么在教程中要同时使用这两个函数?我可以只使用Hibernate验证器库来做同样的事情吗?(我认为我可以使用Hibernate验证器来指定字符串的有效长度,或者不可以)?
因此,当提交登录表单时,它会生成针对
/login**资源的HttpRequest,该资源由声明到控制器类中的此方法处理:

@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 User user。谁传递给它的?我认为这可能取决于我在表单中指定commandName=“user”的事实,因此Spring会自动执行它。它正确吗?
1.据我所知,
@Valid
注解自动调用了验证过程。它是如何发生的?是与Spring提供的AOP特性有关吗?还是什么?为什么这个**@Valid注解只与javax.validation库有关,而与Hibernate验证器无关(所以这个@Valid注解也验证了用Hibernate验证器注解注解的字段?为什么?)
1.根据我的理解,如果用户在登录表单中插入错误值,我可以通过
BindingResult br输入参数获得此错误。使用调试器,我可以看到此对象包含由User**模型对象中定义的注解定义的错误消息。这究竟是如何工作的?

ftf50wuq

ftf50wuq1#

这里是第一个疑问:使用的两个库javax.validationorg.hibernate.validator之间的确切区别是什么?
javax.validation来自于JSR-303 API,您可以查看this Maven dependency中的API类。
Hibernate validator是JSR 303的一个实现(实际上是参考实现),因此它实现了所有API,但添加了自己的扩展,例如您提到的@NotBlank注解。
它将此对象作为输入参数:@Valid User user。谁将它传递给处理程序方法?
Spring MVC中处理程序方法的值是由接口HandlerMethodArgumentResolver的实现提供的。在您的情况下,将被调用来解析User参数的实现可能是ServletModelAttributeMethodProcessor。您可以查看这些类的源代码和JavaDocs,以了解它们在内部是如何工作的。
@Valid注解将自动调用验证过程。这是如何发生的?它使用AOP吗?为什么这个@Valid注解只与javax.validation库相关,而与Hibernate验证器无关(所以这个@Valid注解也验证用Hibernate验证器注解标注的字段?)
验证过程由ModelAttributeMethodProcessor调用,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;
        }
    }
}

如果仔细观察,您将看到以下表达式的condition:

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

这意味着如果参数有以Valid开头的 * 任何注解 *,Spring将调用验证。它可能是JSR 303的@Valid或Spring支持验证组的@Validated。它甚至可以是您的自定义注解,只要它的名称以Valid开头。
根据我的理解,如果用户在登录表单中插入了错误的值,我可以从BindingResult处理程序参数获得此错误。使用调试器,我可以看到此对象包含由用户模型对象中定义的注解定义的错误消息。具体工作原理是什么?
让我们回到ModelAttributeMethodProcessor类,在它的resolveArgument方法中有以下代码:

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

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

    • TLDR**:您在此问题中询问的许多问题都可以追溯到HandlerMethodArgumentResolver的各种实现。我建议使用调试器浏览这些类并逐步浏览它们,以便更好地理解它们。
hgc7kmma

hgc7kmma2#

与两个常用库javax.validation和org.hibernate.validator的区别到底是什么?
Bean Validation是一个Java规范,它允许您通过注解表达对象模型上的约束,允许您以可扩展的方式编写自定义约束。它只是一个根本没有实现的规范。Hibernate Validator是这个规范的一个实现。Hibernate Validator完全实现了Bean Validation,它还具有一些Hibernate独有的特性。简单地说,javax.validation是规范部分,org.hibernate.validator是hib的独特特性。
我可以只使用Hibernate验证器库来做同样的事情吗?(我认为我可以使用Hibernate验证器来指定字符串的有效长度,或者不可以)?
可以,但是最好坚持使用javax.validation名称空间,并且只使用供应商特定名称空间(如org.hibernate.validator)的独特功能。这种方法的好处是,您可以将Bean验证实现从Hibernate Validator切换到其他名称空间***,只需最小的更改***。
为什么在教程中同时使用这两种方法?
因为@NotBlank约束在Bean验证规范中不可用,并且仅由Hibernate验证器提供。
它将此对象作为输入参数:@Valid User user。谁传递给它的?我认为这可能取决于我在表单中指定commandName ="user"的事实,所以Spring会自动执行它。它正确吗?
也许你有GET控制器用于login url,它返回一个空的User对象,然后绑定到spring标签,比如<form:input type="text" path="username" name="username" />,当用户填写表单并提交时,spring收集这些标签值,创建一个User的示例并将其传递给控制器。
为什么这个@Valid注解只与javax. validation库相关,而不与Hibernate验证器相关(所以这个@Valid注解也验证用Hibernate验证器注解的字段?为什么?)
已经解释过了,规范/实施案例!

rjee0c15

rjee0c153#

1.正确,称为属性绑定

  1. Spring 3+支持通过@Valid注解进行JSR303Bean验证,如果类路径上有JSR 303验证器框架的话。
    1.任何违反约束的行为都将在BindingResult对象中显示为错误,因此您必须像现在这样检查控制器方法中的违反行为
    如果(br.getAllErrors().size()〉0){ System.out.println(“服务器端验证发生....”);}
    总之,SpringMVC将使用来自JSP表单(使用Spring的表单标记)的输入,验证由binding its properties之后的@Valid注解所注解的模型对象。任何约束冲突都将在BindingResult对象here is a good post中显示为错误

相关问题