此例为SpringBoot项目
spring-boot-starter-web包含了Hibernate Validator,并且无序配置,lombok是便捷工具,本文中的作用是降低实体类的臃肿度
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
实体类
首先定义实体的验证规则
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.Range;
import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Pattern;
/**
*
DROP TABLE IF EXISTS tb_user;
CREATE TABLE tb_user(
id INT(11) NOT NULL AUTO_INCREMENT,
open_id VARCHAR(50) DEFAULT NULL UNIQUE,
username VARCHAR(50) NOT NULL UNIQUE,
PASSWORD VARCHAR(150) NOT NULL,
gender INT(11) COMMENT '性别,1:男,2:女',
age INT(11),
salt VARCHAR(150) NOT NULL,
email VARCHAR(120) DEFAULT NULL,
phone VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB;
*/
@Data
@Table(name = "tb_user",uniqueConstraints = {
@UniqueConstraint(columnNames = {
"open_id",
"username"
})})
public class User {
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
//可能会有微信登陆
@Column(name = "open_id")
private String openId;
@NotBlank(message = "用户名不能为空")
// @Pattern(regexp = "[A-Za-z0-9_\\-\\u4e00-\\u9fa5]+",message="用户名必须由英文字母、数字、汉字组成")
@Length(min = 1,max = 12,message = "用户名长度为1~12位")
private String username;
@JsonIgnore
@NotBlank(message = "密码不能为空")
@Length(min = 6,max = 12,message = "密码长度为6~12位")
@Pattern(regexp = "[A-Za-z0-9]+",message = "密码必须是英文字母加数字")
private String password;
@Range(min = 1,max = 2,message = "性别只能是1或者2,1代表男性,2代表女性")
private Integer gender;
@Range(min = 0,message = "年龄不能为负数")
private Integer age;
@JsonIgnore
private String salt;
//两种证明身份的方式email,phone
@Email(message = "email地址填写有误")
private String email;
@Length(min = 11,max = 11,message = "手机号码长度有误")
@Pattern(regexp = "0?(13|14|15|18|17)[0-9]{9}",message = "手机号填写有误")
private String phone;
}
此时已将实体校验规则定义完成,对于值为null的属性,hibernate.validator不会去校验。
对从Controller抛出的异常统一处理
import com.xiaoadong.village_managemen.exception.VmException;
import com.xiaoadong.village_managemen.vo.ExceptionResult;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.util.List;
@ControllerAdvice
public class BasicExceptionHandler {
/**
* 处理验证不通过后controller 抛出的异常的处理
* @param e
* @return
*/
@ExceptionHandler(BindException.class)
public ResponseEntity<ExceptionResult> handleBindException(BindException e) {
List<FieldError> fieldErrors = e.getFieldErrors();
// 提取关键错误信息返回给前台
StringBuilder builder = new StringBuilder();
for (FieldError fieldError : fieldErrors) {
builder.append(fieldError.getField());
builder.append(":");
builder.append(fieldError.getRejectedValue());
builder.append(",\n");
builder.append(fieldError.getDefaultMessage());
builder.append("\n");
}
ExceptionResult exceptionResult = new ExceptionResult();
String string = builder.toString();
exceptionResult.setMessage(string);
exceptionResult.setStatus(HttpStatus.BAD_REQUEST.value());
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(exceptionResult);
}
/**
* 自定义异常的处理
* @param e VmException
* @return
*/
@ExceptionHandler(VmException.class)
public ResponseEntity<ExceptionResult> handleVmException(VmException e) {
return ResponseEntity.status(e.getExceptionEnum().value())
.body(new ExceptionResult(e.getExceptionEnum()));
}
}
controller入参测试验证效果
import com.xiaoadong.village_managemen.pojo.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
public class UserController {
// @Autowired
// private UserService userService;
/**
* 通过@Valid,
* 对即将入参使用的SpringMVC封装的对象进行验证
* @param user
*/
@PostMapping("user")
public void insertUser(@Valid User user) {
// userService.insertUser(user);
}
}
对于一些非必填字段,如果为空的话,就没有必要进行验证,然而由于springMVC的封装,将空字符串封装成了"",从而触发了hibernate validator的校验。
自定义手机号码的校验规则
在现有注解不满足我们的需求时,可以使用自定义注解来解决问题
1.编写自定义注解
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE })
@Retention(RUNTIME)
@Constraint(validatedBy = CheckPhoneValidator.class)//此注解要使用的验证器
@Documented
public @interface CheckPhone {
String message() default "手机号填写有误";//默认的错误消息
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default { };
//正则
String value();
@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
@interface List {
CheckPhone[] value();
}
}
2.定义自定义注解的验证器
/**
* ConstraintValidator<CheckPhone, String>
* CheckPhone:注解名
* String:验证的对象类型
*/
public class CheckPhoneValidator implements ConstraintValidator<CheckPhone, String> {
private String regex;
//init时将正则规则保存下来
@Override
public void initialize(CheckPhone constraintAnnotation) {
this.regex = constraintAnnotation.value();
}
@Override
public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
if ( object == null )
return true;
//空的时候也不管它
if (object.equals(""))
return true;
//使用正则验证
return object.matches(regex);
}
}
在实体类中使用注解,将phone字段上加上@CheckPhone
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.xiaoadong.village_managemen.validator.CheckPhone;
import lombok.Data;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.Range;
import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Pattern;
/**
*
DROP TABLE IF EXISTS tb_user;
CREATE TABLE tb_user(
id INT(11) NOT NULL AUTO_INCREMENT,
open_id VARCHAR(50) DEFAULT NULL UNIQUE,
username VARCHAR(50) NOT NULL UNIQUE,
`password` VARCHAR(150) NOT NULL,
gender INT(11) COMMENT '性别,1:男,2:女',
age INT(11),
salt VARCHAR(150) NOT NULL,
email VARCHAR(120) DEFAULT NULL,
phone VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB;
*/
@Data
@Table(name = "tb_user",uniqueConstraints = {
@UniqueConstraint(columnNames = {
"open_id",
"username"
})})
public class User {
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
//可能会有微信登陆
@Column(name = "open_id")
private String openId;
@NotBlank(message = "用户名不能为空")
// @Pattern(regexp = "[A-Za-z0-9_\\-\\u4e00-\\u9fa5]+")
@Length(min = 1,max = 12,message = "用户名长度为1~12位")
private String username;
@NotBlank(message = "密码不能为空")
@Length(min = 6,max = 12,message = "密码长度为6~12位")
@Pattern(regexp = "[A-Za-z0-9]+",message = "密码必须是英文字母加数字")
private String password;
@Range(min = 1,max = 2,message = "性别只能是1或者2,1代表男性,2代表女性")
private Integer gender;
@Range(min = 0,message = "年龄不能为负数")
private Integer age;
@JsonIgnore
//此注解会影响JSON序列化和反序列化因此不太实用,返回的字段最好在数据查询时就决定
private String salt;
//两种证明身份的方式email,phone
@Email(message = "email地址填写有误")
private String email;
// @Pattern(regexp = "0?(13|14|15|18|17)[0-9]{9}",message = "手机号填写有误")
@CheckPhone(value = "0?(13|14|15|18|17)[0-9]{9}")
private String phone;
}
查看效果
不再校验空的phone字段了