通过Hibernate Validator在Controller中方法入参前实现表单验证

此例为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字段了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值