开发中经常会碰到很多校验,如:空校验、长度校验、手机号校验、数值校验等。通过注解去做这些校验能省不少事。
事实上我们也会用这些注解,但有时候自带的注解不能满足我们的业务需求,这时候就需要我们自定义一些注解去使用。
注意:下面代码用到的jar包javax.validation.*来自于SpringBoot。而SpringBoot在2.3.0版本以后就不再引入这个包了。
具体如下:
1.自定义注解
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* 自定义字节长度校验注解
*
* @author ChenHol.Wong
* @create 2020/10/12 21:49
*/
@Documented
@Constraint(validatedBy = ByteLengthValid.class) // 注意这里!
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ByteLength {
int length() default 0; // 定义属性值长度
String message() default "字段长度超长"; // 定义错误提示信息
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2.给自定义注解书写规则,编写一个类实现ConstraintValidator接口,这是个泛型接口,泛型中第一个是自定义的注解,第二个是注解使用的类型。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.io.UnsupportedEncodingException;
/**
* 注解校验规则
*
* @author ChenHol.Wong
* @create 2020/10/12 21:49
*/
public class ByteLengthValid implements ConstraintValidator<ByteLength, String> {
private static final Logger logger = LoggerFactory.getLogger(ByteLengthValid.class);
private int length = 0;
@Override
public void initialize(ByteLength constraintAnnotation) {
length = constraintAnnotation.length(); // 初始化方法,获取注解的属性值
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (length == 0) {
return false;
}
try {
if (value != null) {
return value.trim().getBytes("UTF-8").length <= this.length;
}
} catch (UnsupportedEncodingException e) {
logger.error("获取字节长度异常:", e);
}
return false;
}
}
根据以上定义的规则知道这个注解是校验属性值的字节长度。
使用时在Co'n't'roller的请求中通过@Valid注解开启,如下:
import com.jason.test.project.model.ValidRequest;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* @author ChenHol.Wong
* @create 2020/10/12 21:42
*/
@RestController
@RequestMapping("/valid")
public class TestValidController {
@PostMapping("/login")
public Object test(@RequestBody @Valid ValidRequest request) {
return request.toString();
}
}
这样每次请求的时候都会去校验带有此注解的属性值。如果校验不通过则会返会异常。下面是我通过postman测试
请求实体类:
public class ValidRequest {
@NotEmpty
@ByteLength(length = 5, message = "姓名字段超长")
private String name;
@ByteLength(length = 2, message = "账户字段超长")
private String account;
@ByteLength(length = 10, message = "密码超长")
private String password;
// 省略 getter、setter、toString()
}
请求信息:
返回信息如下:
{
"timestamp": "2020-10-12T14:06:48.005+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"ByteLength.validRequest.password",
"ByteLength.password",
"ByteLength.java.lang.String",
"ByteLength"
],
"arguments": [
{
"codes": [
"validRequest.password",
"password"
],
"arguments": null,
"defaultMessage": "password",
"code": "password"
},
10
],
"defaultMessage": "密码超长",
"objectName": "validRequest",
"field": "password",
"rejectedValue": "12345678901234567",
"bindingFailure": false,
"code": "ByteLength"
}
],
"message": "Validation failed for object='validRequest'. Error count: 1",
"path": "/jason/valid/login"
}
可以通过定义全局异常处理,对MethodArgumentNotValidException异常进行处理,就能得到指定的异常信息。