当前位置: 首页 > article >正文

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的校验框架,使得代码更加简洁和易维护。


http://www.kler.cn/a/385774.html

相关文章:

  • 动态规划-完全背包问题——322.零钱兑换
  • 51单片机基础05 定时器
  • 【JAVA】使用IDEA创建maven聚合项目
  • AtCoder Beginner Contest 380(A-F)
  • Qt 项目架构设计
  • IQ Offset之工厂实例分析
  • golang 实现比特币内核:实现基于椭圆曲线的数字签名和验证
  • 伦敦金价格是交易所公布的吗?
  • 基于Multisim直流稳压电源电路(含仿真和报告)
  • linux查看网络配置
  • 任务中心全新升级,新增分享接口文档功能,MeterSphere开源持续测试工具v3.4版本发布
  • 微服务架构面试内容整理-Archaius
  • 编译器、IDE对C/C++新标准的支持
  • 微服务系列六:分布式事务与seata
  • 游戏提示错误:xinput1_3.dll缺失?四种修复错误的xinput1_3.dll文件
  • JavaScript重定向对网络爬虫的影响及处理
  • LVGL 与 QT
  • 大数据分析案例-基于随机森林算法的智能手机价格预测模型
  • tartanvo ubuntu 20.04部署
  • 云计算基础:AWS入门指南
  • 大数据-214 数据挖掘 机器学习理论 - KMeans Python 实现 算法验证 sklearn n_clusters labels
  • YOLO11改进-模块-引入CMUNeXt Block 增强全局信息
  • 树莓派上安装与配置 Nginx Web 服务器教程
  • 使用 axios 拦截器实现请求和响应的统一处理(附常见面试题)
  • OPPO开源Diffusion多语言适配器—— MultilingualSD3-adapter 和 ChineseFLUX.1-adapter
  • 【Android】ubutun 创建Androidstudio桌面快捷方式