SpringBoot 2.X整合JSR303服务端数据校验

SpringBoot 2.X整合JSR303服务端数据校验

一、JSR-303简介

  • JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,注重于服务端数据校验,官方参考实现是Hibernate Validator。

  • Bean Validation 中内置的 constraint如下:
    在这里插入图片描述
    在这里插入图片描述

二、JSR-303基本校验

  • 声明 constraint 的 JavaBean【User.java】
package com.hf.boot.jsr303.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.URL;

import javax.validation.constraints.*;
import java.io.Serializable;

/**
 * @Copyright (C), 2016-2020 HF
 * @ClassName: User
 * @Author: hf
 * @Date: 2020/7/2 13:40
 * @Description: 用户实体信息
 */
@ApiModel(value = "User对象")
@Data
@Accessors(chain = true)
@NoArgsConstructor
public class User implements Serializable {

    @ApiModelProperty(name = "userId", value = "用户id")
    private Integer userId;


    @NotEmpty(message = "用户名称必须填写")
    @ApiModelProperty(name = "userName", value = "用户名称")
    private String userName;

    @NotEmpty(message = "登录名称必须填写")
    @Length(min = 6,max = 12,message = "登录名称长度在6-12位")
    @ApiModelProperty(name = "loginName", value = "登录名称")
    private String loginName;

    @NotEmpty(message = "登录密码不得为Null")
    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,18}$", message = "密码必须包含数字和字母、且在6-18位之间")
    @ApiModelProperty(name = "password", value = "登录密码")
    private String password;

    @NotNull(message = "Icon头像字段必须填写")
    @URL(message = "Icon链接符合规范")
    @ApiModelProperty(name = "icon", value = "头像")
    private String icon;

    @NotNull(message = "用户邮箱必须填写")
    @Email
    @ApiModelProperty(name = "email", value = "邮箱")
    private String email;

    @ApiModelProperty(name = "phone", value = "手机号")
    private String phone;

    @ApiModelProperty(name = "state", value = "是否启用 0-启用 1-不启用", example = "0")
    private int state;

    @Min(0)
    @Max(10000)
    @NotEmpty
    @ApiModelProperty(name = "sort", value = "排序字段", example = "0")
    private int sort;

    @ApiModelProperty(name = "createTime", value = "注册时间")
    private String createTime;
}

  • 使用@Valid开启校验功能、并获取校验结果。
    /**
     * 功能描述:
     * 〈
     * 用户保存,标注@Valid注解开启校验,给校验的Bean后面紧跟一个BindingResult就可以获取校验的结果。
     * 〉
     *
     * @className: JSR303Controller
     * @author: hf
     * @version: 1.0.0
     * @date: 2020/7/3 11:37
     * @param: [user 用户实体, result 错误消息体]
     * @resp: java.lang.Object
     */
    @PostMapping("/saveUser")
    public Object saveUser(@RequestBody @Valid User user, BindingResult result) {
        HashMap<String, String> errorMap = new HashMap<>();
        result.getFieldErrors().forEach(item -> {

            //1.2 获取错误消息
            String message = item.getDefaultMessage();

            //1.3 获取错误的字段名
            String field = item.getField();

            errorMap.put(field, message);
        });

        if (errorMap.size() > 0) {
            return Resp.fail(errorMap);
        }

        userService.saveUser(user);

        return Resp.ok();

    }
  • 测试校验。
    在这里插入图片描述

三、JSR-303自定义校验

  • 创建自定义注解
package com.hf.boot.jsr303.annotation;

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.RetentionPolicy.RUNTIME;

/**
 * @Copyright (C), 2016-2020 HF
 * @ClassName: ListState
 * @Author: hf
 * @Date: 2020/7/3 13:39
 * @Description: 自定义校验注解-校验用户状态信息
 */
@Documented
@Constraint(validatedBy = {ListStateConstraintValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ListState {

    /**
     * 默认的错误消息【需要新建ValidationMessages.properties文件并配置】
     */
    String message() default "{com.hf.boot.jsr303.annotation.ListState.message}";

    /**
     * 分组配置
     */
    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /**
     * 默认传递的值
     */
    int[] values() default {};
}

  • 自定义校验器
package com.hf.boot.jsr303.annotation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

/**
 * @Copyright (C), 2016-2020 HF
 * @ClassName: ListStateConstraintValidator
 * @Author: hf
 * @Date: 2020/7/3 13:41
 * @Description: 自定义校验校验器、定义规则
 */
public class ListStateConstraintValidator implements ConstraintValidator<ListState, Integer> {


    private Set<Integer> set = new HashSet<>();

    @Override
    public void initialize(ListState constraintAnnotation) {

        //获取注解上可以填的值
        final int[] values = constraintAnnotation.values();

        for (int val : values) {
            set.add(val);
        }
    }


    /**
     * 功能描述:
     * 〈
     * 具体的校验规则定义
     * 〉
     *
     * @className: ListStateConstraintValidator
     * @author: hf
     * @version: 1.0.0
     * @date: 2020/7/3 13:50
     * @param: [value 需要校验的值, context 上下文对象]
     * @resp: boolean
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {

        return set.contains(value);
    }

}

  • 使用注解
@ListState(values = {0, 1})
@ApiModelProperty(name = "state", value = "是否启用 0-启用 1-不启用", example = "0")
private int state;
  • 测试注解。
    在这里插入图片描述

四、JSR-303分组校验

  • 创建分组【SaveGroup.java,UpdateGroup.java】
package com.hf.boot.jsr303.valid;

/**
 * @Copyright (C), 2016-2020 HF
 * @ClassName: SaveGroup
 * @Author: hf
 * @Date: 2020/7/3 14:14
 * @Description: 用于数据保存-分组
 */
public interface SaveGroup {
}

package com.hf.boot.jsr303.valid;

/**
 * @Copyright (C), 2016-2020 HF
 * @ClassName: UpdateGroup
 * @Author: hf
 * @Date: 2020/7/3 14:15
 * @Description: 用于数据修改-分组
 */
public interface UpdateGroup {
}

  • 标注分组
 /**
 * 当用户新增时 userId必须为空、修改时 userId 不得为空
 *
 * 注: 当区分groups时、未标注groups的字段将不再校验
 */
@NotNull(message = "用户修改时【userId】不得为空", groups = {UpdateGroup.class})
@Null(message = "用户新增【userId】必须为空", groups = {SaveGroup.class})
@ApiModelProperty(name = "userId", value = "用户id")
private Integer userId;
  • 标注@Validated注解开启分组校验功能
public Object saveUser(@RequestBody @Validated(SaveGroup.class) User user, BindingResult result) {
  
   //......
  
   return Resp.ok();

 }
public Object updateUser(@RequestBody @Validated(UpdateGroup.class) User user, BindingResult result) {
   
   //......
   
   return Resp.ok();

 }
  • 测试校验
    在这里插入图片描述
    在这里插入图片描述

五、JSR-303 全局异常处理

  • 新建异常处理类【JSR303ExceptionControllerAdvice.java】
package com.hf.boot.jsr303.exception;

import com.hf.boot.api.bean.Resp;
import com.hf.boot.api.constant.SysStatusCodeConstant;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;

/**
 * @Copyright (C), 2016-2020 HF
 * @ClassName: JSR303ExceptionControllerAdvice
 * @Author: hf
 * @Date: 2020/7/2 11:16
 * @Description: JSR303 系统异常 全局处理
 */
@RestControllerAdvice(basePackages = "com.hf.boot.jsr303.controller")
public class JSR303ExceptionControllerAdvice {

    /**
     * 功能描述:
     * 〈
     * 处理全局数据校验异常信息
     * 〉
     *
     * @className: JSR303ExceptionControllerAdvice
     * @author: hf
     * @version: 1.0.0
     * @date: 2020/7/3 15:17
     * @param: [e 参数校验异常]
     * @resp: java.lang.Object
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Object handleValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        HashMap<String, Object> errorMap = new HashMap<>();

        //1.1 获取校验的错误结果
        bindingResult.getFieldErrors().forEach(item -> {

            //1.2 获取错误消息
            String message = item.getDefaultMessage();

            //1.3 获取错误的字段名
            String field = item.getField();

            errorMap.put(field, message);
        });

        return Resp.fail(SysStatusCodeConstant.CHECK_ERROR_CODE, "fail", errorMap);
    }

    /**
     * 功能描述:
     * 〈
     * 处理其它的异常
     * 〉
     *
     * @className: JSR303ExceptionControllerAdvice
     * @author: hf
     * @version: 1.0.0
     * @date: 2020/7/3 15:21
     * @param: [throwable]
     * @resp: java.lang.Object
     */
    @ExceptionHandler(value = Throwable.class)
    public Object handleException(Throwable throwable) {


        return Resp.fail(SysStatusCodeConstant.NO_KNOW_CODE, "未知异常、请联系管理员【" + throwable.getMessage() + "】");
    }
}

  • 控制层只需关注正常调用即可。
public Object updateUser(@RequestBody @Validated(UpdateGroup.class) User user) {
   
   //不需要再指定BindingResult,关注正常逻辑接口调用即可......

   return Resp.ok();

}

六、示例源码地址

  • https://gitee.com/yx971072369/boot-s/tree/master/boot-jsr303
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值