为了提供可靠的API接口,对参数进行校验,保证数据入库的正确性,同事通过添加注释使用Validation规范避免写一大堆的if -else的校验语句。使代码更加简单清晰。
1.1 空和非空校验
#trim()
以后 length 要大于 0 。1.2 数值校验
@DecimalMax(value)
:被注释的元素必须是一个数字,其值必须小于等于指定的最大值。
@DecimalMin(value)
:被注释的元素必须是一个数字,其值必须大于等于指定的最小值。
@Digits(integer, fraction)
:被注释的元素必须是一个数字,其值必须在可接受的范围内。
@Positive
:判断正数。
@PositiveOrZero
:判断正数或 0 。
@Max(value)
:该字段的值只能小于或等于该值。
@Min(value)
:该字段的值只能大于或等于该值。- @Negative
:判断负数。
@NegativeOrZero
:判断负数或 0 。
1.3 Boolean 值检查
@AssertFalse
:被注释的元素必须为 true 。
@AssertTrue
:被注释的元素必须为 false 。
1.4 长度检查
@Size(max, min)
:检查该字段的 size 是否在 min 和 max 之间,可以是字符串、数组、集合、Map 等。
1.5 日期检查
@Future
:被注释的元素必须是一个将来的日期。
@FutureOrPresent
:判断日期是否是将来或现在日期。
@Past
:检查该字段的日期是在过去。
@PastOrPresent
:判断日期是否是过去或现在日期。
1.6 其它检查
@Email
:被注释的元素必须是电子邮箱地址。
@Pattern(value)
:被注释的元素必须符合指定的正则表达式。
@Valid注解,是Bean Validation所定义,支持嵌套校验。可以添加到方法参数,普通方法,成员变量,构造方法上。
@Validated注解,是Spring Validation所定义,可以添加到类,方法参数,普通方法上。同时@Validated有value属性,支持分组校验。
org.springframework.boot
spring-boot-starter-validation
因为 spring-boot-starter-validation
已经引入了 hibernate-validator所以无需重复引入hibernate-validator依赖。
4.1 UserVo实体类
package com.lingyi.mybatis.vo;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.crypto.Mac;
@Data
public class UserVo {
@NotEmpty(message = "登录账号不能为空")
@Length(min = 5,max = 16 ,message = "账号长度为5-16位")
@Pattern(regexp = "^[A-Za-z0-9]+$" ,message = "账号格式为数字和字母")
private String username;
@NotEmpty(message = "密码不能为空")
@Length(min = 4,max = 16,message = "密码为4-16位")
private String password;
}
4.2 LoginCotroller上编写接口,在类上添加@Validated注解,表示LoginCotroller上所有的的接口都需要进行参数校验。
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/login")
@Validated
public class LoginController {
private Logger logger = LoggerFactory.getLogger(getClass());
@PostMapping
public UserVo login(@RequestBody @Validated UserVo userVo){ //在接口参数上使用@Valid和@Validated都可以;
logger.info("登录 login {}",userVo);
return userVo;
}
@GetMapping("/get")
public UserVo get(@RequestParam("id") @Min(value = 0L,message = "编号必须大于0") Integer id){
logger.info("get id = {}",id);
UserVo userVo = new UserVo();
userVo.setPassword("1234567");
userVo.setUsername("张三");
return userVo;
}
}
4.3 启动程序,测试
启动程序后,在浏览器中输入swagger访问地址:http://127.0.0.1:8081/swagger-ui.html 测试对应的接口。通过查看返回结果,果然对对应的字段进行了校验。
后台报错:
测试get接口,输入参数id为-1,查看返回结果:
通过返回的错误信息看到,Validator校验框架返回的错误提示不友好,为了返回更加友好的错误提示,可以通过添加全局异常处理类GlobalExcetionHandler.java
在包com.lingyi.mybatis.exception下创建全局异常处理类GlobalExcetionHandler.java
import jakarta.validation.ConstraintViolationException;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.List;
import java.util.stream.Collectors;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ErrorResponse handleValidationException(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List fieldErrors = result.getFieldErrors();
List errors = fieldErrors.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.toList());
return new ErrorResponse("Validation failed", errors);
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ErrorResponse handleConstraintViolationException(ConstraintViolationException ex) {
List errors = ex.getConstraintViolations().stream()
.map(violation -> violation.getPropertyPath() + ": " + violation.getMessage())
.collect(Collectors.toList());
return new ErrorResponse("Validation failed", errors);
}
@Data
@AllArgsConstructor
private static class ErrorResponse {
private String message;
private List errors;
}
}
在上面的代码中,我们使用@ControllerAdvice注解来定义一个全局的异常处理类。然后我们使用@ExceptionHandler注解来定义处理MethodArgumentNotValidException和ConstraintViolationException异常的方法。在这些方法中,我们可以根据具体的异常类型来处理校验异常,并返回一个包含错误信息的ErrorResponse对象。
这样,当发生校验异常时,Spring Boot会自动调用对应的异常处理方法,并返回统一格式的错误信息给客户端。这样可以使代码更加规范和易于维护。
再次测试返回给前端的错误提示如下: