spring @validated校验,分组校验,自定义校验

严谨的接口是必须要有参数校验的,回避掉一些不合理的请求。但是校验逻辑和正常业务逻辑掺杂在一起,固然能完成校验需求,但是在实现方式上却不那么优雅。比如现在有一个user注册接口:

1.直男癌一般的校验

@PostMapping("/user")
	public Mono<String> insert(@RequestBody User user) {
		if(StringUtils.isEmpty(user.getName())) {
			return Mono.just("姓名不能为空");
		}
		if(StringUtils.isEmpty(user.getEnname())) {
			return Mono.just("英文姓名不能为空");
		}
		// 处理业务逻辑
		return Mono.just("注册成功");
	}

这种最为无脑直接,也确实能完成校验需求,但是却有点憨憨,当一个对象中有很多字段需要校验的时候,很多if - else就显得很臃肿。我刚写代码的时候就经常这样干。。。。

2.借助spring的@validated注解来校验

@Data
public class User {

	private int id;
	
	@NotNull(message = "姓名不可为空")
	private String name;
	
	@NotNull(message = "英文姓名不可为空")
	private String enname;
	
	private String mobile;
	
}


@PostMapping("/user")
	public Mono<String> insert(@RequestBody @Validated User user) {
		
		// 处理业务逻辑
		return Mono.just("注册成功");
	}

借助了spring的力量之后,原先臃肿的if - else没有了,代码瞬间变的简洁了起来。但是这样子也是有一些问题的,比如我们现在要在Controller中新增添一个修改user的接口,既然是修改接口,我们可以选择只修改user的name或者只修改user的enname。那么就会出现一个问题,新增接口的校验和修改接口的校验冲突了。新增接口要求:name和enname都不能为空,修改接口要求:name和enname可以为空,这个时候用一个user对象就有点尴尬。

3.@validated 分组校验


@Data
public class User {

	@Min(value = 1, message = "ID不可为空", groups = {Update.class})
	private int id;
	
	@NotNull(message = "姓名不可为空", groups = {Insert.class})
	private String name;
	
	@NotNull(message = "英文姓名不可为空", groups = {Insert.class})
	private String enname;
	
	private String mobile;
	
}


@PostMapping("/user")
	public Mono<String> insert(@RequestBody @Validated(Insert.class) User user) {
		// 处理新增逻辑
		return Mono.just("注册成功");
	}
	
	@PutMapping("/user")
	public Mono<String> update(@RequestBody @Validated(Update.class) User user) {
		// 处理修改逻辑
		return Mono.just("修改成功");
	}

// 随便自定义2个接口 用于标识新增和修改	
public interface Insert{
	
}
public interface Update{
	
}

通过@Validated的分组校验,可以指定在什么时候才验证字段的合法性。一切看起来都是这么的完美,spring为我们整理了一些内置的验证注解,部分如下:

@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(value) 被注释的元素必须符合指定的正则表达式。
@Email 被注释的元素必须是电子邮件地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty  被注释的字符串必须非空
@Range  被注释的元素必须在合适的范围内

这些自带的验证注解包括验证长度,验证是否为true,验证是否为邮箱,验证最大值等等。但是现在我们的user对象又有一个新的需求,添加电话号码字段,但是spring并没有一个自带的注解是可以验证一个字符串是否是电话号码的。

4.自定义验证注解

第一步,新增一个注解类:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR,
		ElementType.PARAMETER })
//约束注解应用的目标元素类型(METHOD, FIELD, TYPE, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER等)
@Retention(RetentionPolicy.RUNTIME) // 约束注解应用的时机
@Documented
@Constraint(validatedBy = MobileValidator.class)
public @interface IsMobile {
	
	boolean required() default true;

	String message() default "手机号格式不正确";

	Class<?>[] groups() default {};

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

}

 

第二步,编写校验类,实现ConstraintValidator<A extends Annotation, T>接口。ConstraintValidator<A extends Annotation, T>是个泛型接口,第一个泛型是注解类,第二个泛型是需要验证的数据类型。接口有2个方法,一个initialize负责初始化,另外一个isValid负责具体的校验逻辑。

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class MobileValidator implements ConstraintValidator<IsMobile, String>{

	@Override
	public void initialize(IsMobile constraintAnnotation) {
		ConstraintValidator.super.initialize(constraintAnnotation);
	}
	
	@Override
	public boolean isValid(String value, ConstraintValidatorContext context) {
		// 关于手机号的验证偷个懒 不为空且长度不是11位的字符串 就被认为是非法
		if(value != null && value.length() != 11) {
			System.out.println("手机号非法");
			return false;
		}
		return true;
	}

}

第三步, 使用自定义注解:


import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

import com.arvato.ann.Insert;
import com.arvato.ann.IsMobile;
import com.arvato.ann.Update;

import lombok.Data;

@Data
public class User {

	@Min(value = 1, message = "ID不可为空", groups = {Update.class})
	private int id;
	
	@NotNull(message = "姓名不可为空", groups = {Insert.class})
	private String name;
	
	@NotNull(message = "英文姓名不可为空", groups = {Insert.class})
	private String enname;
	
	@IsMobile(message = "手机号格式非法", groups = {Update.class, Insert.class})
	private String mobile;
	
}

至此,项目中大部分的参数验证都能通过注解来完成了,但是也不排除有一些业务场景是校验注解无法完成的,有时候还是需要一些直男癌的if-else的。

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值