如果我使用Kotlin数据类对我的值对象进行建模,那么处理验证的最佳方法是什么。似乎init块是唯一的逻辑位置,因为它在主构造函数之后执行。
data class EmailAddress(val address: String) {
init {
if (address.isEmpty() || !address.matches(Regex("^[a-zA-Z0-9]+@[a-zA-Z0-9]+(.[a-zA-Z]{2,})$"))) {
throw IllegalArgumentException("${address} is not a valid email address")
}
}
}
使用JSR-303示例
这样做的缺点是它需要加载时间编织
@Configurable
data class EmailAddress(@Email val address: String) {
@Autowired
lateinit var validator: Validator
init {
validator.validate(this)
}
}
答案 0 :(得分:1)
我确实发表了评论,但我想我会分享我的验证方法。
首先,我认为对实例执行验证是错误的。这将使反序列化和移交给控制器之间的界限混乱。另外,对我而言,如果您坚持使用干净的体系结构,则验证是核心逻辑的一部分,并且应通过对核心逻辑进行测试来确保它正在发生。
因此,为了让我如愿以偿,我首先定义了自己的核心验证API。纯Kotlin。没有框架或库。保持清洁。
interface Validatable {
/**
* @throws [ValidationErrorException]
*/
fun validate()
}
class ValidationErrorException(
val errors: List<ValidationError>
) : Exception() {
/***
* Convenience method for getting a data object from the Exception.
*/
fun toValidationErrors() = ValidationErrors(errors)
}
/**
* Data object to represent the data of an Exception. Convenient for serialization.
*/
data class ValidationErrors(
val errors : List<ValidationError>
)
data class ValidationError(
val path: String,
val message: String
)
然后我有一个特定于框架的实现。例如javax.validation.Validation
的实现:
open class ValidatableJavax : Validatable {
companion object {
val validator = Validation.buildDefaultValidatorFactory().validator!!
}
override fun validate() {
val violations = validator.validate(this)
val errors = violations.map {
ValidationError(it.propertyPath.toString(), it.message)
}.toMutableList()
if (errors.isNotEmpty()) {
throw ValidationErrorException(errors = errors)
}
}
}
唯一的问题是,javax注释不能与kotlin数据对象一起很好地使用-但这是带有验证的类的示例:
import javax.validation.constraints.Positive
class MyObject(
myNumber: BigDecimal
) : ValidatableJavax() {
@get:Positive(message = "Must be positive")
val myNumber: BigDecimal = myNumber
}
答案 1 :(得分:0)
实际上,看起来验证不是数据类的责任。 data
告诉自己 - 它用于数据存储。
因此,如果您想验证数据类,那么在构造函数的参数上设置@get:
验证并在类中的数据类外部进行验证将负责构造。
你的第二个选择是不使用数据类,只使用简单的类并在构造函数中实现整数逻辑
此外,如果您使用Spring Framework - 您可以使用原型范围创建此类Bean,但使用这种意大利面条代码可能会非常不舒服:)
答案 2 :(得分:0)
在类构造器之外的其他任何地方进行对象创建验证对我来说似乎都是不合理的。这是负责创建的地方,因此应该是定义有效实例和无效实例的规则所在的位置。从维护的角度来看,这对我也很有意义,因为如果我不得不猜测的话,它将是我寻找此类规则的地方。