SpringBoot使用自定义注解对API接口进行数据校验
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;
@Override
public void initialize(ValueCheck constraintAnnotation) {
// 初始化方法,读取注解中的属性值
this.nameMaxLength = constraintAnnotation.nameMaxLength();
this.ageMin = constraintAnnotation.ageMin();
this.ageMax = constraintAnnotation.ageMax();
}
@Override
public 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) {
// 如果转换失败,返回false
context.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的校验框架,使得代码更加简洁和易维护。