项目DEMO - 编写一个自定义校验注解

目录

开始

未来实现效果

第一步:编写自定义校验注解

第二步:编写自定义校验器

第三步:编写配置文件

效果演示


开始


未来实现效果

编写一个 @ListValue 注解,可以实现功能有:

  • 限定字段的值,例如指定只能为 1 或 2.
  • 支持自定义错误信息.
  • 支持分组校验.

第一步:编写自定义校验注解

import jakarta.validation.Constraint
import jakarta.validation.Payload
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.annotation.AnnotationTarget.*
import kotlin.reflect.KClass

@MustBeDocumented
@Constraint(validatedBy = [ListValueConstraintValidator::class])
@Target(FUNCTION, FIELD, ANNOTATION_CLASS, CONSTRUCTOR, VALUE_PARAMETER, TYPE)
@Retention(RUNTIME)
annotation class ListValue(
    val message: String = "{org.cyk.gulimall.product.infra.config.exception.ListValue.message}",
    val groups: Array<KClass<*>> = [],
    val payload: Array<KClass<out Payload>> = [],

    val vals: IntArray = []
)

a)注解解释: 

  • @MustBeDocumented

    这个注解表示使用该注解的注解应该包含在生成的Kotlin文档中。换句话说,当使用KDoc生成文档时,应用了 MustBeDocumented 的注解将会出现在文档中。

  • @Constraint(validatedBy = [ListValueConstraintValidator::class])

    这个注解定义了该注解是一个校验约束,并且指定了用来校验该注解的逻辑类。ListValueConstraintValidator::class 是一个自定义校验类(下文中详细讲)

  • @Target

    这个注解定义了自定义注解可以应用的目标元素。可以是方法、字段、注解类型、构造函数、参数或类型使用。在Kotlin中,它们分别对应 FUNCTION, FIELD, ANNOTATION_CLASS, CONSTRUCTOR, VALUE_PARAMETER, TYPE 这样可以控制自定义注解可以应用于哪些代码元素。

  • @Retention(RUNTIME)

    这个注解定义了自定义注解的保留策略.  RUNTIME表示注解将在运行时保留,因此可以通过反射机制访问注解信息。其他可选的保留策略包括 SOURCE(只在源代码中保留)和 CLASS(在编译时保留,但在运行时不可见)。

b)成员解释:

Note:message、groups、payload 在自定义校验注解中必须要有,因为是 JSR303 中的规范(可以参考其他校验注解,例如 @NotBlank)

  • message:自定义错误信息去哪个地方取(下文中会详细讲解).
  • groups:支持分组校验功能.
  • payload:支持自定义负载信息.
  • vals:vals 是自定义的,用来给校验注解传递值,例如 @ListValue(vals = [1, 2]).

第二步:编写自定义校验器

上面提到的 @Constraint(validatedBy = [ListValueConstraintValidator::class]) 中的 ListValueConstraintValidator 就是自定义校验器,负责处理校验核心逻辑.

例如你是这样使用注解的@ListValue(vals=[1,2]) 

那么 initialize 方法就可以拿到其中 vals,进行初始化(逻辑自定义)

isValid 方法就是将来使用时触发的逻辑

import jakarta.validation.ConstraintValidator
import jakarta.validation.ConstraintValidatorContext

class ListValueConstraintValidator: ConstraintValidator<ListValue, Int> {

    private lateinit var set: Set<Int>

    /**
     * 初始化方法
     */
    override fun initialize(constraintAnnotation: ListValue) {
        set = constraintAnnotation.vals.toSet()
    }

    /**
     * @param p0: 需要校验的值
     */
    override fun isValid(p0: Int, p1: ConstraintValidatorContext): Boolean {
        return set.contains(p0)
    }

}

Ps:@Constraint(validatedBy = [ ... ]) 中还可以指定多个不同的校验器,来适配不同类型的校验

第三步:编写配置文件

在编写自定义校验注解时,里面有一个成员 message,他就会去配置文件(ValidationMessages.properties)找这个配置.

因此这里我们在 resource 目录下需要创建一个 ValidationMessages.properties 配置文件,编写内容如下:

org.cyk.gulimall.product.infra.config.exception.ListValue.message=must be a specified value~

Ps:key 就是 message 默认值(一般就取 自定义校验注解的全限定类名.message ),value 就是校验错误提示的默认信息

效果演示

a)demo 如下:

    @GetMapping("/value")
    fun getValue(
        @RequestBody @Valid dto: ValueDto
    ): ApiResp<String> {
        return ApiResp.ok("ok")
    }


data class ValueDto (
    @field:ListValue(vals = [1, 2])
    val status: Int
)

a)当输入错误的值时.

Ps:这个默认错误信息,也可以在统一异常处理中,专门提取出来.

b)输入的值正确时

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈亦康

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值