前言
在项目开发中,后端对于传参进行校验不可缺少,如何实现呢?除了在代码里面使用if…else…外,本文列举了两种对不同形式的传参进行校验方式,分别是body实体对象(包括map集合)、param参数。
本文项目是基于spring boot开发,版本是2.3.7,pom文件中要先引入spring-boot-starter-validation
依赖,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
如果Spring Boot版本是 2.3 1 之前, spring-boot-starter-web
中,已经包含spring-boot-starter-validation
,不需要我们再手动添加!
Body实体对象形式
无论是Body实体对象还是Param参数,都需要在类(controller)上面添加 @Validated
注解。
对于有@RequestBody
注解的实体对象参数,需要在参数上添加@Valid
注解,如果验证失败,它将抛出 MethodArgumentNotValidException
异常。默认情况下,Spring 会将此异常转换为 HTTP Status 400(错误请求)。
@RestController
@RequestMapping("/api/person")
@Validated
public class PersonController {
@PostMapping
public ResponseEntity<PersonRequest> save(@RequestBody @Valid PersonRequest personRequest) {
return ResponseEntity.ok().body(personRequest);
}
}
对于需要校验的参数,在实体类中进行校验,如下
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PersonRequest {
@NotNull(message = "classId 不能为空")
private String classId;
@Size(max = 33)
@NotNull(message = "name 不能为空")
private String name;
@Pattern(regexp = "(^Man$|^Woman$|^UGM$)", message = "sex 值不在可选范围")
@NotNull(message = "sex 不能为空")
private String sex;
}
正则表达式说明:
^string
: 匹配以 string 开头的字符串string$
:匹配以 string 结尾的字符串^string$
:精确匹配 string 字符串(^Man$|^Woman$|^UGM$)
: 值只能在 Man,Woman,UGM 这三个值中选择
Param参数
对于get请求传参,只需要直接在对应参数前面添加校验注解即可。
@Controller
@Validated
public class ValidationController {
@GetMapping("/validate1")
@ResponseBody
public String validate1(
@Size(min = 1,max = 10,message = "姓名长度必须为1到10")@RequestParam("name") String name,
@Min(value = 10,message = "年龄最小为10")@Max(value = 100,message = "年龄最大为100") @RequestParam("age") Integer age,
@Future @RequestParam("birth")@DateTimeFormat(pattern = "yyyy-MM-dd hh:mm:ss") Date birth){
return "validate1";
}
}
全局捕获异常
上面已经对参数进行校验,但是对于校验结果需要返回前端,这里就需要创建全局异常捕获类GlobalExceptionHandler
,将异常信息封装成前端需要的格式返回,如下。
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 用来处理validation异常,捕获Param参数校验异常信息
* @param ex
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public WebResult resolveConstraintViolationException(ConstraintViolationException ex){
WebResult errorWebResult = new WebResult(WebResult.FAILED);
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
if(!CollectionUtils.isEmpty(constraintViolations)){
StringBuilder msgBuilder = new StringBuilder();
for(ConstraintViolation constraintViolation :constraintViolations){
msgBuilder.append(constraintViolation.getMessage()).append(",");
}
String errorMessage = msgBuilder.toString();
if(errorMessage.length()>1){
errorMessage = errorMessage.substring(0,errorMessage.length()-1);
}
errorWebResult.setInfo(errorMessage);
return errorWebResult;
}
errorWebResult.setInfo(ex.getMessage());
return errorWebResult;
}
/**
* 用来处理validation异常,捕获Body实体属性参数校验异常信息
* @param ex
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public WebResult resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex){
WebResult errorWebResult = new WebResult(WebResult.FAILED);
List<ObjectError> objectErrors = ex.getBindingResult().getAllErrors();
if(!CollectionUtils.isEmpty(objectErrors)) {
StringBuilder msgBuilder = new StringBuilder();
for (ObjectError objectError : objectErrors) {
msgBuilder.append(objectError.getDefaultMessage()).append(",");
}
String errorMessage = msgBuilder.toString();
if (errorMessage.length() > 1) {
errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
}
errorWebResult.setInfo(errorMessage);
return errorWebResult;
}
errorWebResult.setInfo(ex.getMessage());
return errorWebResult;
}
}
常用的注解校验:
JSR303
定义了 Bean Validation(校验)的标准, validation-api
并没有提供实现。 Hibernate Validation
是对这个规范/规范的实现,并且增加了 @Email
、 @Length
、 @Range
等注解。 Spring Validation
底层依赖的就是 Hibernate Validation
。
JSR 提供的校验注解:
@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(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator 提供的校验注解:
@NotBlank(message =) 非 null,且长度必须大于 0,只用于验证String类型参数
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty String、Collection、Map、数组是不能为null或长度不为0
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
至此,参数校验就实现了,但是如果官方提供的校验注解不满足业务需要,你也可以自定义校验注解。
自定义校验注解
创建约束注解类
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { FlagValidatorClass.class})
public @interface FlagValidator{
String message() default "parameter is not correct";
String[] value() default {"少林","武当"};
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default {};
}
@Constraint(validatedBy = { FlagValidatorClass.class})
用来指定处理这个注解逻辑的类
创建注解校验类
public class FlagValidatorClass implements ConstraintValidator<FlagValidator, String> {
private String[] values;
/**
* 用于初始化注解上的值到这个validator
* @param constraintAnnotation
*/
@Override
public void initialize(FlagValidator flagValidator) {
values =flagValidator.value();
}
/**
* 具体的校验逻辑
* @param value
* @param context
* @return
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
List<String> list = Arrays.asList(values);
return list.contains(value);
}
}
现在你就可以使用@FlagValidator
这个注解了,无论是放在Param参数上还是Body实体中。
参考:
https://www.jianshu.com/p/89a675b7c900