SpringBoot使用自定义注解对API接口进行数据校验
前言
在Spring Boot中,使用自定义注解进行数据校验是一种非常灵活和强大的方式。本文将详细介绍如何创建自定义注解 @ValueCheck
,并将其应用于控制器方法中进行数据校验。我们将增加对姓名长度、年龄范围以及姓名非法字符的校验逻辑,并创建一个全局异常处理器来处理校验失败的情况。
1. 创建自定义注解
首先,我们创建一个自定义注解 @ValueCheck
,用于标记需要校验的字段。我们将增加对姓名长度、年龄范围以及姓名非法字符的支持。
package com.example.annotation;import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;/*** 自定义注解 @ValueCheck,用于标记需要校验的字段。* 注解可以放在字段或方法参数上,指定校验逻辑。*/
@Documented
@Constraint(validatedBy = ValueCheckValidator.class)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValueCheck {/*** 默认错误信息。* @return 错误信息*/String message() default "值不符合要求";/*** 分组,用于多组校验规则时指定当前注解属于哪一组。* @return 分组*/Class<?>[] groups() default {};/*** 额外的数据负载,通常用于传递给校验器更多上下文信息。* @return 数据负载*/Class<? extends Payload>[] payload() default {};/*** 姓名长度的最大值,默认为20。* @return 姓名长度的最大值*/int nameMaxLength() default 20;/*** 年龄的最小值,默认为0。* @return 年龄的最小值*/int ageMin() default 0;/*** 年龄的最大值,默认为150。* @return 年龄的最大值*/int ageMax() default 150;
}
2. 创建校验器
接下来,我们创建一个校验器 ValueCheckValidator
,用于实现具体的校验逻辑。我们将增加对姓名长度、年龄范围以及姓名非法字符的校验。
package com.example.annotation;import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;/*** 自定义校验器 ValueCheckValidator,用于实现 @ValueCheck 注解的具体校验逻辑。*/
public class ValueCheckValidator implements ConstraintValidator<ValueCheck, String> {private int nameMaxLength;private int ageMin;private int ageMax;@Overridepublic void initialize(ValueCheck constraintAnnotation) {// 初始化方法,读取注解中的属性值this.nameMaxLength = constraintAnnotation.nameMaxLength();this.ageMin = constraintAnnotation.ageMin();this.ageMax = constraintAnnotation.ageMax();}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {// 实现具体的校验逻辑if (value == null || value.isEmpty()) {return false;}// 检查姓名长度if (value.length() > nameMaxLength) {context.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate("姓名长度超过最大允许长度 " + nameMaxLength).addConstraintViolation();return false;}// 检查姓名是否只包含中文字符if (!value.matches("[\\u4e00-\\u9fa5]+")) {context.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate("姓名必须为中文字符").addConstraintViolation();return false;}// 尝试将值转换为整数,检查年龄范围try {int age = Integer.parseInt(value);if (age < ageMin || age > ageMax) {context.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate("年龄必须在 " + ageMin + " 到 " + ageMax + " 之间").addConstraintViolation();return false;}} catch (NumberFormatException e) {// 如果转换失败,返回falsecontext.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate("无效的年龄格式").addConstraintViolation();return false;}// 示例校验逻辑:值必须以字母开头return Character.isLetter(value.charAt(0));}
}
3. 使用自定义注解
在控制器方法中使用自定义注解 @ValueCheck
来校验API接口的值。我们将演示如何在校验姓名长度、年龄范围以及姓名非法字符时使用注解。
package com.example.controller;import com.example.annotation.ValueCheck;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;import javax.validation.constraints.NotNull;/*** 示例控制器,展示了如何使用 @ValueCheck 注解来校验API接口的值。*/
@RestController
@RequestMapping("/api")
@Validated
public class MyController {/*** 示例API接口,使用 @ValueCheck 注解校验请求参数。** @param name 姓名* @param age 年龄* @return 成功响应*/@PostMapping("/check-value")public ResponseEntity<String> checkValue(@RequestParam @NotNull @ValueCheck(nameMaxLength = 20) String name,@RequestParam @NotNull @ValueCheck(ageMin = 0, ageMax = 150) String age) {return ResponseEntity.ok("值符合要求");}
}
4. 全局异常处理
为了处理校验失败的情况,我们可以创建一个全局异常处理器来捕获并处理校验异常。
package com.example.exception;import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;import java.util.HashMap;
import java.util.Map;/*** 全局异常处理器,用于处理校验失败的情况。*/
@RestControllerAdvice
public class GlobalExceptionHandler {/*** 处理 MethodArgumentNotValidException 异常。** @param ex 校验异常* @return 包含错误信息的响应*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {Map<String, String> errors = new HashMap<>();ex.getBindingResult().getAllErrors().forEach((error) -> {String fieldName = ((FieldError) error).getField();String errorMessage = error.getDefaultMessage();errors.put(fieldName, errorMessage);});return ResponseEntity.badRequest().body(errors);}
}
总结
通过上述步骤,我们创建了一个自定义注解 @ValueCheck
,并在控制器方法中使用它来校验API接口的值是否符合特定条件。我们增加了对姓名长度、年龄范围以及姓名非法字符的校验逻辑,并创建了一个全局异常处理器来处理校验失败的情况。这种方式不依赖于拦截器和AOP,而是利用了Spring的校验框架,使得代码更加简洁和易维护。