springboot数据校验——Hibernate Validator

1、导包

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>6.0.1.Final</version>
</dependency>

2、注解说明

在这里插入图片描述
在这里插入图片描述

3、需要数据校验的地方

两种请求:

  • get、delete等请求,参数形式为RequestParam/PathVariable
  • post、put等请求,参数形式为RequestBoty

RequestParam/PathVariable形式的参数校验

Get、Delete请求一般会使用RequestParam/PathVariable形式参数参数,这种形式的参数校验一般需要以下两个步骤,如果校验失败,会抛出ConstraintViolationException异常。

必须在Controller类上标注@Validated注解;
在接口参数前声明约束注解(如@NotBlank等)

@RestController
@Validated
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;
    
	@GetMapping("/{id}")
	@MyLog("获取用户")
	public R user(@Max(value = 12,message = "用户id不能为空") @PathVariable("id")Integer id,
	              @NotBlank(message = "用户名不能为空") @RequestParam("username") String username){
	    User user = userService.getById(id);
	    return R.ok().put("data",user);
	}

	@PostMapping("/add")
    public R add( @RequestBody Teacher teacher){
        ValidatorUtils.validateObject(teacher, AddGroup.class);
        System.out.println(JSONUtil.toJsonStr(teacher));
        return R.ok();
    }

}

RequestBoty形式的参数校验

POST、PUT请求一般会使用requestBody传递参数,这种情况下,在入参对象上添加@Validated注解就能实现自动参数校验。

比如,有一个保存用户信息的接口,要求username长度是2-20位,password字段长度是8-20位,还要加上邮箱校验。如果校验失败,会抛出MethodArgumentNotValidException异常,Spring默认会将其转为400(Bad Request)请求.

分组校验

背景:在执行保存和更新操作的时候,校验的参数可能存在差异,比如保存的时候不需要校验Id,而更新的时候就需要校验id(主键),写两个实体类复用率会很低。所以这个时候分组校验就很有必要

/**
 * 新增数据 Group
 */
public interface AddGroup {
}

/**
 * 更新数据 Group
 */
public interface UpdateGroup {
}

实体类

@Data
public class Teacher {
    
    @NotNull(message = "id不能为空",groups = UpdateGroup.class)
    private Integer id;
	
	//这里注意要使用 org.hibernate.validator.constraints.NotBlank,不要导错类
    @NotBlank(message = "教师账号不能为空",groups = {UpdateGroup.class, AddGroup.class})
    private String tid;

    @NotBlank(message = "密码不能为空",groups = {UpdateGroup.class, AddGroup.class})
    private String pwd;

    @NotBlank(message = "教师姓名不能为空",groups = {UpdateGroup.class, AddGroup.class})
    private String tname;

    @Email(message = "教师邮箱不合法",groups = {UpdateGroup.class, AddGroup.class})
    private String email;

    @NotNull(message = "学生信息不能为空",groups = {UpdateGroup.class, AddGroup.class})
    @Valid //数据嵌套要加上,要不然嵌套的不会校验
    private List<Student> students;
    
}

@Data
public class Student {

    @NotNull(message = "id不能为空",groups = UpdateGroup.class)
    private Integer id;

    @NotBlank(message = "学生账号不能为空",groups = {UpdateGroup.class, AddGroup.class})
    private String sid;

    @NotBlank(message = "密码不能为空",groups = {UpdateGroup.class, AddGroup.class})
    private String pwd;

    @NotBlank(message = "学生姓名不能为空",groups = {UpdateGroup.class, AddGroup.class})
    private String sname;

    @Email(message = "学生邮箱不合法",groups = {UpdateGroup.class, AddGroup.class})
    private String email;

}

分组校验工具类

public class ValidatorUtils {
    private static final ValidatorFactory VALIDATOR_FACTORY = Validation.buildDefaultValidatorFactory();
    /**
     * 参数校验
     *
     * @param paramObject 需要校验的参数
     */
    public static void validateObject(Object paramObject) {
        Validator validator = VALIDATOR_FACTORY.getValidator();
        Set<ConstraintViolation<Object>> validateResult = validator.validate(paramObject);
        if (CollectionUtils.isEmpty(validateResult)) {
            return;
        }

        StringBuilder errorMessageSb = new StringBuilder();
        for (ConstraintViolation<Object> violation : validateResult) {
            errorMessageSb.append(violation.getMessage()).append(";");
        }
        throw new RRException(errorMessageSb.toString());
    }

    /**
     * 参数校验【支持分组校验】
     *
     * @param paramObject 需要校验的参数
     * @param classes     具体的分组
     */
    public static void validateObject(Object paramObject, Class<?> classes) {
        Validator validator = VALIDATOR_FACTORY.getValidator();
        Set<ConstraintViolation<Object>> validateResult = validator.validate(paramObject, classes);
        if (CollectionUtils.isEmpty(validateResult)) {
            return;
        }

        Set<String> errorMsgSet = new HashSet<>();
        for (ConstraintViolation<Object> violation : validateResult) {
            errorMsgSet.add(violation.getMessage());
        }
        throw new RRException(String.join("<br>", errorMsgSet));
    }

    private ValidatorUtils() {
    }
}

自定义异常处理类

/**
 * 自定义异常
 *
 * 
 */
public class RRException extends RuntimeException {
	private static final long serialVersionUID = 1L;
	
    private String msg;
    private int code = 500;
    
    public RRException(String msg) {
		super(msg);
		this.msg = msg;
	}
	
	public RRException(String msg, Throwable e) {
		super(msg, e);
		this.msg = msg;
	}
	
	public RRException(String msg, int code) {
		super(msg);
		this.msg = msg;
		this.code = code;
	}
	
	public RRException(String msg, int code, Throwable e) {
		super(msg, e);
		this.msg = msg;
		this.code = code;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}
	
}

全局异常处理类

/**
 * 异常处理器
 *
 *
 */
@RestControllerAdvice
@Log4j
public class RRExceptionHandler {
	private Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * 处理自定义异常
	 */
	@ExceptionHandler(RRException.class)
	public R handleRRException(RRException e){
		R r = new R();
		r.put("code", e.getCode());
		r.put("msg", e.getMessage());
		return r;
	}

	/**
	 * 方法参数校验异常
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
		logger.error("[全局异常处理] [参数校验不通过]{}", e.getMessage(), e);
		return R.error(e.getMessage());
	}


	/**
	 * 方法参数校验异常[类型不配备]
	 */
	@ExceptionHandler(UnexpectedTypeException.class)
	public R handleUnexpectedTypeException(UnexpectedTypeException e) {
		logger.error("[全局异常处理] [参数校验类型不匹配]{}", e.getMessage(), e);
		return R.error(e.getMessage());
	}

	/**
	 * 方法RequestParam/PathVariable形式参数校验异常
	 */
	@ExceptionHandler(ConstraintViolationException.class)
	public R handleConstraintViolationException(ConstraintViolationException e) {
		Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
		List<String> msgList = new ArrayList<>();
		for (ConstraintViolation<?> constraintViolation : constraintViolations) {
			msgList.add(constraintViolation.getMessageTemplate());
		}
		return R.error(String.join("<br>",msgList));
	}


	@ExceptionHandler(Exception.class)
	public R handleException(Exception e){
		logger.error(e.getMessage(), e);
		return R.error();
	}
}

返回类

public class R extends HashMap<String, Object> {
    private static final long serialVersionUID = 1L;

    public R() {
        put("code", 200);
        put("msg", "success");
    }

    public static R error() {
        return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "未知异常,请联系管理员");
    }

    public static R error(String msg) {
        return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg);
    }

    public static R error(int code, String msg) {
        R r = new R();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    public static R ok(String msg) {
        R r = new R();
        r.put("msg", msg);
        return r;
    }

    public static R ok(Map<String, Object> map) {
        R r = new R();
        r.putAll(map);
        return r;
    }

    public static R ok() {
        return new R();
    }

    public R put(String key, Object value) {
        super.put(key, value);
        return this;
    }

    public Integer getCode() {
        return (Integer) get("code");
    }
}

使用注解方式:

/**
     * 走参数校验注解的 groups 组合校验
     *
     * @param userDTO
     * @return
     */
    @PostMapping("/update/groups")
    public RspDTO update(@RequestBody @Validated(Update.class) UserDTO userDTO) {
        userService.updateById(userDTO);
        return RspDTO.success();
    }


全局异常捕获MethodArgumentNotValidException。将其返回

@ExceptionHandler(MethodArgumentNotValidException.class)
    public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e){
        List<ObjectError> allErrors = e.getAllErrors();
        R r = new R();
        r.put("code", -1);
        for (ObjectError allError : allErrors) {
            r.put("msg", allError.getDefaultMessage());
            //只返回第一个错误提示
            break;
        }
        return r;
    }

参考
https://blog.csdn.net/WEDUEST/article/details/121594610

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值