前言
数据校验是交互式网站一个不可或缺的功能,前端的js校验可以涵盖大部分的校验职责,如用户名唯一性,生日格式,邮箱格式校验等等常用的校验。但是为了避免用户绕过浏览器,使用http工具直接向后端请求一些违法数据,服务端的数据校验也是必要的,可以防止脏数据落到数据库中等等。
一、数据校验的两种方式
1、基于注解形式的数据校验
JSR提供的校验注解:
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator提供的校验注解:
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
2、基于Validator接口形式的数据校验
(1)Validator的实现类:需实现Validator接口的supports方法和validate方法。
Supports方法用于判断当前的Validator实现类是否支持校验当前需要校验的实体类,只有当supports方法的返回结果为true的时候,该Validator接口实现类的validate方法才会被调用来对当前需要校验的实体类进行校验。
Validate 方法用于定义校验规则。
(2)ValidationUtils类:校验工具类,提供了校验实体类、实体字段的方法,返回一个自定义的校验对象。
(3)ValidationResult类:校验返回结果类
二、数据校验实战
项目结构如下:
1、基于Validator接口形式的数据校验
创建一个POJO类,即数据校验的实体
package com.scb.spittr.springValidator;
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable {
private String username;
private String sex;
private Integer age;
}
创建Validator的实现类
package com.scb.spittr.springValidator;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
public class UserValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
//对上诉的User进行数据校验
return User.class.equals(clazz);
}
//对目标类target进行校验,并将校验错误记录在errors中
//Errors是Spring用来存放错误信息的对象
@Override
public void validate(Object object, Errors errors) {
// 验证username、sex和age是否为null
ValidationUtils.rejectIfEmpty(errors, "username", null, "用户名不能为空");
ValidationUtils.rejectIfEmpty(errors, "sex", null, "性别不能为空");
ValidationUtils.rejectIfEmpty(errors, "age", null, "年龄不能为空");
}
}
进行Spring MVC配置
package com.scb.spittr.springValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc
@ComponentScan("com.scb.spittr.springValidator")
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// TODO Auto-generated method stub
super.addResourceHandlers(registry);
}
}
控制层
package com.scb.spittr.springValidator;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.DataBinder;
import org.springframework.validation.Errors;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping(value = "/user")
public class UserController {
@RequestMapping(value="/userForm",method=RequestMethod.GET)
public String userForm(Model model){
User user=new User();
// model中添加属性user,值是user对象
model.addAttribute("user",user);
return "userForm";
}
//使用DataBinder来设定当前Controller需要使用的Validator
//@initBinder可以直接在你的controller中提供数据绑定(被注解方法不能有返回值,即返回void)
@InitBinder
public void InitBinder(DataBinder binder){
// 设置验证的类为UserValidator
binder.setValidator(new UserValidator());
}
//在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加Errors(或其子类BindingResult)用来接收校验出错信息
@RequestMapping(value="/register",method=RequestMethod.POST)
public String register(@Validated User user, Errors errors){
// 如果Errors对象有Field错误的时候,重新跳回注册页面,否则正常提交
if (errors.hasFieldErrors())
return "userForm";
return "submit";
}
}
前端界面
userForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>基于Validator接口的验证</title>
</head>
<body>
<h3>注册页面</h3>
<form:form modelAttribute="user" method="post" action="register" >
<table>
<tr>
<td>姓名:</td>
<td><form:input path="username"/></td>
<td><font color="red"><form:errors path="username"/></font></td>
</tr>
<tr>
<td>性别:</td>
<td><form:input path="sex"/></td>
<td><font color="red"><form:errors path="sex"/></font></td>
</tr>
<tr>
<td>年龄:</td>
<td><form:input path="age"/></td>
<td><font color="red"><form:errors path="age"/></font></td>
</tr>
<tr>
<td><input type="submit" value="注册"/></td>
</tr>
</table>
</form:form>
</body>
</html>
submit.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>success</title>
</head>
<body>
<h3>验证成功页面</h3>
</body>
</html>
运行结果:(当提交null时,将得到以下结果)
2、基于注解形式的数据校验
创建Student数据校验对象
package com.scb.spittr.javaValidation;
import lombok.Data;
import javax.validation.constraints.*;
@Data
public class Student {
@Min(value = 100000,message = "sno must be greater than or equal to {value}")
private Integer sno;
@Size(min = 2,max = 30,message = "sname must be between {min} and {max} characters long.")
private String sname;
@DecimalMin(value = "10",message = "age must be greater than or equal to {value}")
private int sage;
}
Student的控制层
package com.scb.spittr.javaValidation;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping(value = "/student")
public class StudentController {
@RequestMapping(value = "/stuForm", method = RequestMethod.GET)
public String stuForm(Model model) {
Student student = new Student();
// model中添加属性student,值是student对象
model.addAttribute("student", student);
return "stuForm";
}
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String register(@Validated Student student, Errors errors) {
// 如果Errors对象有Field错误的时候,重新跳回注册页面,否则正常提交
if (errors.hasFieldErrors())
return "stuForm";
return "submit";
}
}
stuForm.jsp界面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>使用注解形式的验证</title>
</head>
<body>
<h3>注册页面</h3>
<form:form modelAttribute="student" method="post" action="register" >
<table>
<tr>
<td>学号:</td>
<td><form:input path="sno"/></td>
<td><font color="red"><form:errors path="sno"/></font></td>
</tr>
<tr>
<td>姓名:</td>
<td><form:input path="sname"/></td>
<td><font color="red"><form:errors path="sname"/></font></td>
</tr>
<tr>
<td>年龄:</td>
<td><form:input path="sage"/></td>
<td><font color="red"><form:errors path="sage"/></font></td>
</tr>
<tr>
<td><input type="submit" value="注册"/></td>
</tr>
</table>
</form:form>
</body>
</html>
运行结果:
3、自定义校验注解
创建自定义的密码注解
package com.scb.spittr.annotation;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target( { METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordValidator.class)
@Documented
public @interface Password {
String message() default "{密码必须是5~10位数字和字母组合}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
package com.scb.spittr.annotation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PasswordValidator implements ConstraintValidator<Password, String> {
//5~10位的数字与字母组合
private static Pattern pattern = Pattern.compile("(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{5,10}");
public void initialize(Password constraintAnnotation) {
//do nothing
}
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return false;
}
Matcher m = pattern.matcher(value);
return m.matches();
}
}
创建数据校验对象
package com.scb.spittr.annotation;
import lombok.Data;
import javax.validation.constraints.Pattern;
@Data
public class Login {
@Pattern(regexp = "^[1-9]\\d*$",message = "id must be positive integer")
private String id;
//使用自定义的数据校验注解
@Password
private String password;
}
控制层
package com.scb.spittr.annotation;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/login")
public class LoginController {
@RequestMapping(value = "loginForm",method = RequestMethod.GET)
public String loginForm(Model model) {
Login login = new Login();
// model中添加属性login,值是login对象
model.addAttribute("login", login);
return "loginForm";
}
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String register(@Validated Login login, Errors errors) {
// 如果Errors对象有Field错误的时候,重新跳回注册页面,否则正常提交
if (errors.hasFieldErrors())
return "loginForm";
return "submit";
}
}
前端loginForm.jsp界面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>自定义校验标签测试</title>
</head>
<body>
<h3>注册页面</h3>
<form:form modelAttribute="login" method="post" action="register" >
<table>
<tr>
<td>ID:</td>
<td><form:input path="id"/></td>
<td><font color="red"><form:errors path="id"/></font></td>
</tr>
<tr>
<td>Password:</td>
<td><form:input path="password"/></td>
<td><font color="red"><form:errors path="password"/></font></td>
</tr>
<tr>
<td><input type="submit" value="注册"/></td>
</tr>
</table>
</form:form>
</body>
</html>
运行结果: