我有一个 dto 类,我正在对其应用类级别自定义约束。我的问题是我希望它在创建和更新 api 时表现不同。在属性验证的情况下,使用不同的注释很容易实现,但在类级别上,我在寻找正确的解决方案方面遇到了一些问题。这是我想要实现的目标的一个简约示例:
interface Create
interface Update
@ValidApiDto(groups = [Create::class, Update::class])
data class ApiDto(
val id: Long,
val metaData: MetaDataDto,
// many other properties
)
@Constraint(validatedBy = [ApiDtoValidator::class])
@Target(allowedTargets = [AnnotationTarget.CLASS])
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class ValidApiDto(
val message: String = "Invalid api dto!",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = []
)
class ApiDtoValidator: ConstraintValidator<ValidApiDto, ApiDto> {
override fun isValid(dto: ApiDto, context: ConstraintValidatorContext): Boolean {
// My logic comes here. Adapt behavior based on update or create
return true
}
}
@RestController
@Validated
class MyRestController {
@PostMapping("/post")
@Validated(value = [Create::class])
fun post(@Valid dto: ApiDto): ResponseEntity<*>? {
return null
}
@PutMapping("/put")
@Validated(value = [Update::class])
fun update(@Valid dto: ApiDto): ResponseEntity<*>? {
return null
}
}
一个验证器中是否可以有不同的逻辑?
我试图通过上下文找到传递的组,但它给了我所有可能的组,在我的情况下创建和更新。
验证组是过滤约束的方法,即将约束分组并根据条件执行组中的约束。
如果您想要一个条件验证器实现,您可以做的是:
@Constraint(validatedBy = [ApiDtoValidator::class])
@Target(allowedTargets = [AnnotationTarget.CLASS])
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class ValidApiDto(
val message: String = "Invalid api dto!",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = [],
// add some enum, or a boolean, whatever you find more suitable:
val type: ConstraintType = ConstraintType.CREATE
)
@ValidApiDto(type = ConstraintType.CREATE, groups = [Create::class])
@ValidApiDto(type = ConstraintType.UPDATE, groups = [Update::class])
data class ApiDto(
val id: Long,
val metaData: MetaDataDto,
// many other properties
)
通过这种方式,您可以将具有不同配置的约束分配给不同的验证组,并且只有相应的约束将应用于该组。
在约束验证器的实现中,您将可以访问注释属性:
class ApiDtoValidator: ConstraintValidator<ValidApiDto, ApiDto> {
override fun initialize(ValidApiDto constraintAnnotation) {
// access your configured type value:
val type constraintAnnotation.type();
}
override fun isValid(dto: ApiDto, context: ConstraintValidatorContext): Boolean {
// My logic comes here. Adapt behavior based on update or create
return true
}
}