spring MVC组件开发-表单数据校验

       在实际工作中,得到数据后的第一步就是校验数据的正确性,如果存在录入上的问题,一般先经过前端js进行验证,但是前端有多种方式可以绕过前端js验证(不是安全有效的),为了数据安全性一般还需要在服务器端做数据校验。这个时候就可以使用spring所提供的验证器(Validator)规则去验证。

        所有的验证都是要先注册验证器,不过验证器也是springmvc自动加载的。我们使用JSR 303校验规范,hibernate提供的数据校验器进行数据校验。

使用JSR 303注解验证输入内容

        Spring  提供了对Bean的功能校验,通过注解@Valid标明哪个Bean需要启用注解式的验证。在javax.validation.constrains.*中定义了一系列的JSR 303规范给出的注解:

限制

说明

@Null

限制只能为null

@NotNull

限制必须不为null

@AssertFalse

限制必须为false

@AssertTrue

限制必须为true

@DecimalMax(value)

限制必须为一个不大于指定值的数字

@DecimalMin(value)

限制必须为一个不小于指定值的数字

@Digits(integer,fraction)

限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction

@Future

限制必须是一个将来的日期

@Max(value)

限制必须为一个不大于指定值的数字

@Min(value)

限制必须为一个不小于指定值的数字

@Past

限制必须是一个过去的日期

@Pattern(value)

限制必须符合指定的正则表达式

@Size(max,min)

限制字符长度必须在min到max之间

@Email

邮箱类型

@NotEmpty

集合,不为空

@NotBlank

不为空字符串

@Positive

数字,正数

@PositiveOrZero

数字,正数或0

@Negative

数字,负数

@NegativeOrZero

数字,负数或0

@PastOrPresent

过去或者现在日期

@FutrueOrPresent

将来或者现在日期

为了使用这些注解,假设要完成一个保存雇员表单:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
  
<!DOCTYPE html>  
<html>  
<head>  
    <title>添加员工</title>  
</head>  
<body>  
    <form action="${pageContext.request.contextPath }/control/emp/save" method="post" >  
            用户名:<input type="text" name="username" placeholder="请输入用户名,最小4位,最大12位" required/>  
            <span></span>  
            <br/>  
            密码:<input type="password" name="password" placeholder="请输入登录密码"/><br/>  
            电子邮箱:<input type="email" name="email" placeholder="请输入电子邮箱"/><br/>  
            员工生日:<input type="date" name="bornDate" /><br/>  
            薪水:<input type="number" name="salary" placeholder="请输入员工薪水" /><br/>  
            联系电话:<input type="text" name="phone" placeholder="请输入员工联系电话"  pattern="^1[358]\d{9}$"/><br/>  
            
            员工简介:  
        <textarea placeholder="请输入员工简介" name="intro"></textarea>   
        <br/>  
        <input type="submit" value="提交" /> <input type="reset">  
    </form>  
      
</body>  
</html>  
校验规则:
  • 户名,密码,电子邮箱,入职日期不能为空
  • 入职日期格式为yyyy-MM-dd,且只能大于今日
  • 生日日期格式为yyyy-MM-dd,且只能是一个过去日期
  • 邮箱需要符合格式:
  •  简介内容不得多于256个字符
  • 薪水最小值为2000,最大值为5万
  • 联系方式需为正确的手机号码(使用正则表达式匹配)

建立pojo,确定校验规则:

public class Employee {  
    private Integer id;  
    /** 
     * 用户名,不允许为空,最小4位,最大12位 
     */  
    @NotBlank(message = "用户名不能为空")  
    @Size(min = 4,max = 12,message = "{employee.username.valid.size.message}")  
    private String username;  
    /** 
     * 密码:不能为空,最小6位 
     */  
    @NotBlank(message = "密码不能为空")  
    @Size(min = 6,max = 12,message = "密码最低6位")  
    private String password;  
      
    /** 
     * 电子邮箱,不能为空,要满足邮箱基本格式 
     */  
    @NotNull(message = "邮箱不能为空")  
    @Email(message="邮箱必须满足基本格式")  
    private String email;  
    /** 
     * 员工生日,格式为:yyyy-MM-dd 
     * 必须是一个过去的日期 
     */  
    @DateTimeFormat(iso = ISO.DATE)  
    @Past(message = "你不能录用还未出生的员工")  
    private LocalDate bornDate;  
      
    /** 
     * 入职日期,格式为:yyyy-MM-dd 
     * 必须是一个现在或者将来的日期 
     */  
    @DateTimeFormat(iso = ISO.DATE)  
    @FutureOrPresent  
    private LocalDate entryDate = LocalDate.now();  
    /** 
     * 员工联系(手机)电话 
     * 必须符合手机号码格式(正则表达式) 
     */  
    @Pattern(regexp = "^1[358]\\d{9}$",message = "请输入正确的手机号码")  
    private String phone;  
    /** 
     * 员工薪水,最低2000,最高5万 
     */  
    @Min(value = 2000,message="工资不能低于2000,否则麻烦了")  
    @Max(value=50000,message="工资不能大于50000,否则交奢侈税")  
    private float salary;  
      
    /** 
     * 员工简介,最大不超过200 
     */  
    @Size(min = 6,max = 12,message = "员工简介不能超过200字")  
    private String intro;  
  
       //***************setter and getter*************/  
}  
       这样就定义了一个pojo,用于接收表单的信息。字段上面的注解反映了对每一个字段的验证要求,这样就可以加入对应校验,如果没有指定message属性,会生成默认的错误信息。message配置项用来定义当校验失败后的错误信息,这样就能启动Spring的检验规则来校验表单了。

用控制器验证表单

@Controller  
@RequestMapping("/control/emp")  
public class EmployeeController {  
    @RequestMapping("/save")  
    public String save(@Validated Employee emp,BindingResult errors) {  
        if(errors.hasErrors()) {  
            return "emp/emp_save";  
        }  
        return "redirect:./list";  
    }  
}  
        使用了注解@Validated 标明这个Bean将会被校验,另外一个类型为BindingResult的参数(或者为Errors)是用于保存校验是否存在错误信息的,也就是当采用JSR 303规范进行校验后,它会将这个错误信息保存到这个参数中,在方法中判断是否有错误信息,如果有错误信息,跳转到填写表单页面告诉用户错误信息。

如何在jsp页面中显示错误信息呢?这时可以使用jsp提供给我们的标签库

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
<span style="background-color: #fafafa; font-family: monospace;"><%@ taglib prefix="form"  uri="http://www.springframework.org/tags/form"%></span>  
<!DOCTYPE html>  
<html>  
<head>  
    <title>添加员工</title>  
</head>  
<body>  
    <form:form action="${pageContext.request.contextPath }/control/emp/save" method="post"  modelAttribute="employee">  
            用户名:<input type="text" name="username" placeholder="请输入用户名,最小4位,最大12位" required/>  
            <span><span style="background-color: #fafafa; font-family: monospace;"><form:errors path="username" cssStyle="color: red"/></span></span>  
            <br/>  
            密码:<input type="password" name="password" placeholder="请输入登录密码"/><br/>  
            电子邮箱:<input type="email" name="email" placeholder="请输入电子邮箱"/><br/>  
            员工生日:<input type="date" name="bornDate" /><br/>  
            薪水:<input type="number" name="salary" placeholder="请输入员工薪水" /><br/>  
            联系电话:<input type="text" name="phone" placeholder="请输入员工联系电话"  pattern="^1[358]\d{9}$"/><br/>  
            
            员工简介:  
        <textarea placeholder="请输入员工简介" name="intro"></textarea>   
        <br/>  
        <input type="submit" value="提交" /> <input type="reset">  
    </form:form>  
</body>  
</html>  
看起来似乎非常不错,但是我们的message错误提示信息是硬编码在pojo身上,为了避免其硬编码而实现可配置,我们在src/main/resource下新建messageSource.properties文件:
employee.username.valid.notnull.message=用户名不能为空  
employee.username.valid.size.message=用户名不能少于4位且不能超过12位 
上面简单罗列了两个错误信息配置,其它道理一样,哪怎么应用(读取)指定的错误信息呢?在pojo验证规则之上:
/** 
* 用户名,不允许为空,最小4位,最大12位 
*/  
@NotBlank(message = "{employee.username.valid.notnull.message}")  
@Size(min = 4,max = 12,message = "{employee.username.valid.size.message}")  
private String username;  

 可以采用{}表达式对配置文件的key加以读取其对应的value。其它字段同理,到这里还不能读取指定配置文件,需要告诉spring的检验器加载什么样的配置文件,配置代码如下:

@Configuration  
@ComponentScan(basePackages = "com.wise.tiger.web")  
@EnableWebMvc  
public class WebConfig implements WebMvcConfigurer{  
    @Bean  
    public InternalResourceViewResolver viewResolver() {  
        var viewResolver = new InternalResourceViewResolver();  
        viewResolver.setPrefix("/WEB-INF/pages/");  
        viewResolver.setSuffix(".jsp");  
        return viewResolver;  
    }  
  
    @Override //静态资源不被前端控制器拦截  
    public void addResourceHandlers(ResourceHandlerRegistry registry) {  
        registry.addResourceHandler("/static/**")//添加静态资源的url-pattern  
            .addResourceLocations("/static/");  
    }  
    @Bean  
    public ReloadableResourceBundleMessageSource messageResource() {  
        var messageResource = new ReloadableResourceBundleMessageSource();  
        //如果不指定,默认会去classpath下的messages.properties配置文件  
        messageResource.setBasename("classpath:resourceMessage");  
        return messageResource;  
    }  
      
    /**  
     * 添加表单校验器  
     */  
    @Override  
    public Validator getValidator() {  
        var validator = new LocalValidatorFactoryBean();  
        validator.setProviderClass(HibernateValidator.class);  
        validator.setValidationMessageSource(messageResource());  
        return validator;  
    }  
}  

 分组校验

1.定义分组:可以采用标识接口来进行分组区分

//分组校验1  
public interface ValidationGroup1 {  
}  
  
//分组校验2  
public interface ValidationGroup2 {  
}

2.在检验规则上添加分组

/** 
* 用户名,不允许为空,最小4位,最大12位 
*/  
@NotBlank(message = "{employee.username.valid.notnull.message}",groups = ValidationGroup1.class)  
@Size(min = 4,max = 12,message = "{employee.username.valid.size.message}",groups = ValidationGroup2.class)  
private String username;</span>  

3.在conroller中指定使用的分组校验

@Controller  
@RequestMapping("/control/emp")  
public class EmployeeController {  
    @RequestMapping("/save")  
    public String save( @Validated(ValidationGroup2.class) @ModelAttribute Employee emp,BindingResult errors,Model model) {  
        if(errors.hasErrors()) {  
            for (var i = 0; i < errors.getErrorCount();i++) {  
                System.out.println(errors.getObjectName() + ":" + errors.getFieldError().getDefaultMessage());  
            }  
            return "emp/emp_save";  
        }  
        return "redirect:./list";  
    }  
}  

 有时候除了简单的输入格式、非空性等校验,也需要一定的业务校验,Spring提供了Validator接口来实现校验,它将在进入控制器逻辑之前对参数的合法性进行校验。核心代码如下:

public interface Validator {  
    /** 
     * 判断当前校验器是否用于检验clazz类型的pojo 
     * @param clazz -- pojo类型 
     * @return true 启动校验,false 不校验 
     */  
    @Override  
    public boolean supports(Class<?> clazz) {  
        return false;  
    }  
  
    /** 
     * 检验pojo的合法性 
     * @param target 请求对象 
     * @param errors 错误信息 
     */  
    @Override  
    public void validate(Object target, Errors errors) {  
          
    }  
  
}  

Validator接口是SpringMvc校验表单的核心接口,它只是一个验证器,在Spring中最终被注册到验证器的列表中,这样就可以提供给各个控制器去定义,然后通过supports方法判定是否会启用验证器去验证数据。对于校验的过程,则是通过validate方法去实现的。

package com.wise.tiger.pojo;  
  
import org.springframework.validation.Errors;  
import org.springframework.validation.Validator;  
  
/** 
 * @Description: 员工校验器 
 * @author: <a href="mailto:1020zhaodan@163.com">Adan</a>  
 * @date: 2019年5月28日  下午7:23:40 
 * @version:1.0-snapshot 
 */  
public class EmployeeValidator implements Validator {  
    /** 
     * 判断当前校验器是否用于检验clazz类型的pojo 
     * @param clazz -- pojo类型 
     * @return true 启动校验,false 不校验 
     */  
    @Override  
    public boolean supports(Class<?> clazz) {  
        //判断验证是否为Employee,如果是则进行校验  
        return Employee.class.equals(clazz);  
    }  
  
    /**
     * 检验pojo的合法性:提交过来的薪水不能低于职位的最低薪资
     * @param target 请求对象 (employee)
     * @param errors 错误信息
     */
    @Override
    public void validate(Object target, Errors errors) {
        var emp = (Employee)target;
        if(emp.getSalary() < emp.getJob().getLowSalary()){
            errors.rejectValue("job",null,String.format("薪水%.2f低于%s的最低薪资水平:%.2f",emp.getSalary(), emp.getJob().getValue(),emp.getJob().getLowSalary()));
        }
    }  
}  

在Employee实体上添加职位属性字段,该字段为枚举类型,定义如下:

@Getter
@RequiredArgsConstructor
public enum JOB {
    CLERK(2000,"业务员"),
    SALESMAN(2000,"销售员"),
    ANALYST(5000,"分析员"),
    SEARCHER(3000,"搜索员"),
    MANAGER(6000,"经理"),;
    @NonNull
    private float lowSalary;
    @NonNull
    private String value;
}
//================================Employee===========================
@NotNull(message = "{employee.job.notnull.message}")
private JOB job;

//================================jsp================================
  职位:<select name="job">
  <option value="CLERK">业务员</option>
  <option value="SALESMAN">销售员</option>
  <option value="ANALYST" selected="selected">分析员</option>
  <option value="SEARCHER">搜索员</option>
  <option value="MANAGER">经理</option>
  </select>
  <span>
    <spring:errors cssStyle="color: red;font-size: 12px" path="job"/>
  </span>
  <br/>

需要将该验证器捆绑到对应的控制器中,Spring MVC提供了注解@InitBinder,通过该注解就可以将验证器和控制器捆绑在一起,这样就能对请求表单进行验证了。

@Controller  
@RequestMapping("/control/emp")  
public class EmployeeController {  
    @InitBinder  
    public void initBinder(DataBinder binder){  
        //数据绑定器加入验证器  
        binder.setValidator(new EmployeeValidator());  
    }  
    @RequestMapping("/save")  
    public String save( @Validated(ValidationGroup2.class) @ModelAttribute Employee emp,
BindingResult errors,Model model) {  
        if(errors.hasErrors()) {  
            for (var i = 0; i < errors.getErrorCount();i++) {  
                System.out.println(errors.getObjectName() + ":" + errors.getFieldError().getDefaultMessage());  
            }  
            return "emp/emp_save";  
        }  
        return "redirect:./list";  
    }  
}  

这样就能对比较复杂的逻辑关系进行校验了。@InitBinder的使用还有其它内容,后续讲解。。。。。。

附:依赖列表

dependencies {  
    compile group: 'org.springframework', name: 'spring-context', version: '5.1.7.RELEASE'  
    compile group: 'org.springframework', name: 'spring-webmvc', version: '5.1.7.RELEASE'  
    compile group: 'org.springframework', name: 'spring-test', version: '5.1.7.RELEASE'  
    providedCompile group: 'javax.servlet.jsp', name: 'javax.servlet.jsp-api', version: '2.3.3'  
    providedCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'  
    compile group: 'taglibs', name: 'standard', version: '1.1.2'  
    compile group: 'javax.servlet', name: 'jstl', version: '1.2'  
    compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'  
    compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'  
    compile group: 'org.mybatis', name: 'mybatis-spring', version: '2.0.1'  
    compile group: 'org.mybatis', name: 'mybatis', version: '3.5.1'  
    compile group: 'com.alibaba', name: 'druid', version: '1.1.16'  
    compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.16'  
    compile group: 'org.springframework', name: 'spring-jdbc', version: '5.1.7.RELEASE'  
    compile group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final'  
    compile group: 'org.hibernate', name: 'hibernate-validator', version: '6.0.16.Final'  
    compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.9'  
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值