深入理解Spring MVC开发-数据校验

前言

上面在处理器逻辑中谈到了参数的转换,转换参数出来之后,紧跟着往往是验证参数的合法性,因此Spring MVC也提供了验证参数的机制。一方面,它可以支持JSR-303注解验证,在默认的情况下Spring Boot会引入Hibernate Validator机制来支持JSR-303验证规范。另一方面,因为业务会比较复杂所以需要自定义验证机制。Spring MVC提供了相关的验证机制,这便是本节需要讨论的问题。

1. JSR-303验证

JSR-303验证主要通过注解的方式进行,这里先定义一个需要验证的POJO,此时需要在其属性中加入相关的注解,代码如下:

package cn.hctech2006.boot.bootmvc.bean;

import org.hibernate.validator.constraints.Range;
import org.springframework.format.annotation.DateTimeFormat;

import javax.validation.constraints.*;
import java.util.Date;

/**
 * 验证POJO
 */
public class ValidatorPojo {
    @NotNull(message = "id不能为空")
    private Long id;
    @Future(message = "需要一个未来的日期")//只能是将来的日期
    //@Past 只能是过去的日期
    @DateTimeFormat(pattern = "yyyy-MM-dd")//日期格式转换
    @NotNull//不能为空
    private Date date;
    @NotNull//不能为空
    @DecimalMin(value = "0.1")//浮点数最小值
    @DecimalMax(value = "10000.00")//浮点数最大值
    private Double doubleValue;
    @NotNull//不能为空
    @Min(value = 1, message = "最小值为1")//整数最小值
    @Max(value = 88,message = "最大值为88")//整数最大值
    @NotNull//不能为空
    private  Integer integer;
    @Range(min = 1, max = 888, message = "范围是1-888")//长整型范围
    private Long range;
    @Email(message = "邮箱格式错误")//邮箱格式
    private String email;
    @Size(min = 20, max = 30, message = "字符串长度要求在20到30之间")//字符串长度
    private String size;

//getter and setter
}

POJO中的属性带来各种各样验证注解,并且代码已经在注释中说明其作用,JSR-303验证就是通过这些注解来执行验证的。为了测试需要编写一个html,然后使用JSON数据请求发送这个对象给控制器,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试JSR-303</title>
    <script src="https://code.juqery.com/jquery-3.2.0.js"/>
    <script type="text/javascript">
        $(document).ready(function(){
            //请求验证的POJO
            var pojo = {
                id: null,
                date: '2017-08-08',
                doubleValue: 999999.09,
                integer: 100,
                range: 1000,
                email: email,
                size: 'adv1212',
                regexp: 'a,b,c,d'
            }
            $.post({
                url: "./validate",
                //此处需要告知传递参数类型为JSON,不能缺少
                contentType: "application/json",
                //将data转换为字符串传递
                data: JSON.stringify(pojo),
                //成功后的方法
                success: function(result){

                }
            });
        });
    </script>
</head>
<body>

</body>
</html>

这样打开这个页面的时候,他就会通过Ajax请求到对应的方法,然后提供注解来进行验证。为了打开这个项目并且提供后台验证,在MyController中新增方法用来响应这个页面发出的Ajax请求,代码如下:

    /**
     * 映射HTML页面
     * @return
     */
    @GetMapping("/valid/page")
    public String validPage(){
        return "/role/pojo";
    }
     * 解析验证参数错误
     * @param vp 需要验证的POJO,使用注解@Valid来表示验证。
     * @param errors 错误信息,他由Spring MVC通过验证POJO后自动填充。
     * @return
     */
    @RequestMapping("/valid/validate")
    public Map<String, Object> validate(@Valid @RequestBody ValidatorPojo vp, Errors errors){
        Map<String, Object> errMap = new HashMap<>();
        //获取错误列表
        List<ObjectError> oes = errors.getAllErrors();
        for(ObjectError oe: oes){
            String key = null ;
            String msg = null;
            //字段错误
            if(oe instanceof FieldError){
                FieldError fe = (FieldError) oe;
                key = fe.getField();//获取错误验证字段名
            }else{
                //非字段错误
                key=oe.getObjectName();//获取验证对象名
            }
            //错误信息
            msg=oe.getDefaultMessage();
            errMap.put(key,msg);

        }
        return errMap;
    }

代码中使用@RequestBody代表接受的是一个JSON参数,这样Spring 就会通过页面获取Ajax提交的JSON请求体,然后@Valid注解则表示启动验证机制,这样Spring就会启动JSR303验证机制进行验证。他会自动的将最后的验证结果放入到Errors对象中,这样就可以从中获取相关验证过后的信息。

这里不妨对JSR-303的机制进行测试。运行Spring Boot启动文件之后,在浏览器地址输入一下URLhttp://localhost:8243/my/valid/page,然后就可以观察运行结果,如图所示:

在这里插入图片描述
显然这里的验证成功了。但是有时候验证规则并不是这么简单,比如一些业务逻辑的验证。例如,需要验证购买商品的总价格,那么应该是:总价格=单价×数量,这样的逻辑验证就不能通过JSR-303验证了。为此Spring 还提供了自己的验证机制,下面来介绍他们

2. 参数验证机制

为了更加灵活的提供验证机制,Spring还提供了自己的验证机制。在参数转换时,可以看到在Spring MVC中,存在WebDataBinder机制进行管理,在默认的情况下Spring会自动的根据上下文通过注册了的转换器转换出控制器所需要的参数。在WebDataBinder中除了可以注册转换器之外,还允许注册验证器(Validator)。
在Spring控制器中,它还允许使用注解@InitBinder,这个注解的作用就是允许进入控制器方法之前修改WebDataBinder机制。下面在验证机制和日期格式绑定的场景下演示,不过在此之前稍微认识一下Spring MVC的验证机制。在Spring MVC中,定义了一个接口Validator,这个接口的源代码如下


package org.springframework.validation;

public interface Validator {
//判断该验证器是否支持该Class类型的验证
    boolean supports(Class<?> var1);
//如果supports为true,则这个方法执行验证逻辑
//var1是被验证的POJO对象
    void validate(Object var1, Errors var2);
}

这就是Spring所定义的验证器接口,他定义了两个方法,其中suppoprts方法参数为需要验证的POJO类型,如果该方法返回true,则Spring会使用验证器的validate方法去验证POJO。ervalidate方法包含需要的target对象和错误对象errors,其中target是参数绑定后的POJO,这样便可以通过这个参数对象进入业务逻辑的自定义验证。如果盘发现错误,则可以保存到erros对象中,然后返回给控制器。下面以实例进行说明,这里先定义用户验证器,如代码清单所示,他会对用户对象和用户名进行非空判断。

package cn.hctech2006.boot.bootmvc.validator;

public class SysRoleValidator implements Validator {
    //验证器只支持SysRole类型验证
    @Override
    public boolean supports(Class<?> aClass) {
        return aClass.equals(SysRole.class);
    }

    //验证逻辑
    @Override
    public void validate(Object o, Errors errors) {
        //对象为空
        if(o == null){
            //直接在参数处报错,这样就不能进入控制器方法
            errors.reject("", null, "角色不能为空");
            return;
        }
        //强制类型转换
        SysRole sysRole = (SysRole) o;
        //用户非空串
        if(StringUtils.isEmpty(sysRole.getName())){
            //增加错误,可以进入用户名方法
            errors.rejectValue("name", null, "用户名不能为空");
        }
    }
}

有了这个验证器,Spring还不会自动启动他,因为还没有绑定给WebDataBinder机制。在SpringMVC中提供了一个注解@InitBinder,他的作用是在执行控制器方法之前,处理器会先执行表@InitBinder标注的方法。这时可以将WebDataBinder对象作为参数传递到方法中,通过这层关系得到WebDataBinder对象,这个对象有一个setValidator方法,它可以绑定自定义的验证器,这样就可以在获取参数之后,通过自定义的验证器去验证参数,只是WebDataBinder除了可以绑定验证器外,还可以对参数进行自定义,例如,不使用@DataTimeFormat获取日期参数。假设还继续使用代码清单中的StringToSysRoleConverter转换器,使用一下代码来测试验证器和设置日志格式。

    @InitBinder
    public void  initBinder(WebDataBinder webDataBinder){
        //绑定验证器
        webDataBinder.setValidator(new SysRoleValidator());
        //定义时间参数格式,参数不再需要注解@DateTimeFormat,boolean参数表示是否允许为空
        webDataBinder.registerCustomEditor(Date.class, new CustomDateEditor(
                new SimpleDateFormat("yyyy-MM-dd"),false));

    }

    /**
     *
     * @param sysRole 角色用户使用StringTOConverter转换
     * @param errors 验证器返回错误
     * @param date 因为WebDataBinder已经绑定格式,所以不需要在注解
     * @return 各类数据
     */
         @GetMapping("/validator")
    @ResponseBody

    public Map<String, Object> validator(@Valid SysRole sysRole, Errors errors, Date date){
        Map<String, Object> map = new HashMap<>();
        map.put("sysRole", sysRole);
        map.put("date",date);
        //判断是否存在错误
        if(errors.hasErrors()){
            //获取全部错误
            List<ObjectError> oes = errors.getAllErrors();
            for(ObjectError oe:oes){
                //判断是否字段错误
                if(oe instanceof FieldError){
                    FieldError fe = (FieldError) oe;
                    map.put(fe.getField(), fe.getDefaultMessage());
                }else{
                    //对象错误
                    map.put(oe.getObjectName(), oe.getDefaultMessage());
                }
            }

        }
        return map;
    }

这里的initBinder方法会因为标注注解@INitBinder,因此会在控制器方法前被执行,并且将WebDataBinder对象传递进去,在这个方法里面绑定了自定义的验证器SysRoleValidator,而且设置日期的格式,所以在控制器方法中已经不再需要使用@DateTimeFormat去定义日期格式化,通过这样的自定义,再使用注解@Valid标注SysRole参数之后,Spring MVC就会去遍历对应的验证器,当遍历到SysRoleValidator之后,会去遍历对应的验证器,当便利到SysRoleValidator之时,绘制性他的supports方法。因为该方法会返回true,所以Spring MVC会用这个验证器去验证SysRole类型的数据。对于日期类型也指定了对应的格式,这样控制器的Date类型的参数也不需要在使用注解的协作。
这里还需要关注一下控制器方法的Errors参数。他是Spring MVC通过验证器验证后得到的错误信息,由Spring MVC验证完验证规则后进行传递。这里首先判断是否存在错误,如果存在错误,则便利错误,然后将错误信息收入map之中,因为方法标注了@ResponseBody,所以最终会转化为JSON响应请求。
下面输入http://localhost:8243/role/validator?sysRole=-sdad&&date=2020-02-05.请注意,这里的name已经传递为空,所以在进行角色验证的时候会存在错误信息的显示这个请求的截图如图所示
在这里插入图片描述
从上图中可以看出,用户名的验证已经成功,也就是说验证器已经起作用了,而且日期也是成功的,他返回了一个日期参数

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值