我们如何在 Collection 上进行 kotlin 数据类验证?

Posted

技术标签:

【中文标题】我们如何在 Collection 上进行 kotlin 数据类验证?【英文标题】:How do we do kotlin data class validation on Collection? 【发布时间】:2019-09-16 03:25:20 【问题描述】:

我正在尝试在 kotlin 中向我的数据模型添加验证,使用 @field 注释可以轻松完成简单的字段。但是,我正在努力对收藏做同样的事情。

我已将问题上传到 github here

java 模型运行没有问题,但 kotlin 版本没有。我在这里添加两个模型。

public class JavaUser 
    @NotEmpty
    @NotNull
    @Pattern(regexp = "[a-z]*", message = "Only lower case first name")
    private String name;

    private List<
            @NotNull
            @NotEmpty
            @Pattern(regexp = "\\d10", message = "Only 10 digits")
                    String> phones;

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public List<String> getPhones() 
        return phones;
    

    public void setPhones(List<String> phones) 
        this.phones = phones;
    

data class KotlinUser(
    @field:NotEmpty
    @field:NotNull
    @field:Pattern(regexp = "[a-z]*", message = "Only lower case first name")
    val name: String,

    // Cannot use @field here, anything else we could use?
    val phones: List<
        @NotNull
        @NotEmpty
        @Pattern(regexp = "\\d10", message = "Only 10 digits")
        String>
)

我的测试 - java 测试通过,但 kotlin 测试失败

    @Test
    fun `java user validation`() 
        val javaUser = JavaUser()
        javaUser.name = "sadfjsjdfhsjdf"
        javaUser.phones = listOf("dfhgd")

        webTestClient.put().uri("/user/java")
            .body(BodyInserters.fromObject(javaUser))
            .exchange()
            .expectStatus().is4xxClientError
    

    @Test
    fun `kotlin user validation`() 
        val kotlinUser = KotlinUser(name = "sadfjsjdfhsjdf", phones = listOf("dfhgd"))

        webTestClient.put().uri("/user/kotlin")
            .body(BodyInserters.fromObject(kotlinUser))
            .exchange()
            .expectStatus().is4xxClientError
    

控制器

@RestController
class Controller 
    @PutMapping("/user/java")
    fun putUser(@RequestBody @Valid javaUser: JavaUser): Mono<ResponseEntity<String>> =
        Mono.just(ResponseEntity("shouldn't get this", HttpStatus.OK))

    @PutMapping("/user/kotlin")
    fun putUser(@RequestBody @Valid kotlinUser: KotlinUser): Mono<ResponseEntity<String>> =
        Mono.just(ResponseEntity("shouldn't get this", HttpStatus.OK))

任何帮助将不胜感激。谢谢!

【问题讨论】:

您的/kotlin 端点可能有问题 我不这么认为。如果您想查看,我已添加控制器 您应该尝试检查生成的字节码以查看您的注释所在的位置(有an IntelliJ feature for that)。我敢打赌,@field: 的缺失会产生一个无注释的 getter 类型。问题是,我不确定您是否可以在 Kotlin 属性类型中的类型参数上获得注释,最终得到生成的 getter 类型的类型参数。 看来 Kotlin 不支持。注释的目标应该是TYPE_PARAMETER,但它是unsupported yet。查看this问题 谢谢@caco3 【参考方案1】:

一种解决方法是在控制器中使用来自 spring 的 @Validated

例子:

@RestController
@RequestMapping("/path")
@Validated
class ExampleController 

    @PostMapping
    fun registerReturns(@RequestBody @Valid request: List<@Valid RequestClass>) 
        println(request)
    


请求类:

@JsonIgnoreProperties(ignoreUnknown = true)
data class RequestClass(
        @field:NotBlank
        val field1: String?,
        @field:NotBlank
        @field:NotNull
        val field2: String?)

以这种方式应用 Beans 验证。

【讨论】:

【参考方案2】:

试试这个:

data class KotlinUser(
    @field:NotEmpty
    @field:NotNull
    @field:Pattern(regexp = "[a-z]*", message = "Only lower case first name")
    val name: String,

    @field:Valid
    @field:NotEmpty
    val phones: List<Phone>
)

data class Phone(
    @NotBlank
    @Pattern(regexp = "\\d10", message = "Only 10 digits")
    val phoneNumber: String?
)

【讨论】:

【参考方案3】:

如果您不坚持使用 Bean Validation,YAVI 可以作为替代方案。

您可以按如下方式定义验证:

import am.ik.yavi.builder.ValidatorBuilder
import am.ik.yavi.builder.forEach
import am.ik.yavi.builder.konstraint

data class KotlinUser(val name: String,
                      val phones: List<String>) 
    companion object 
        val validator = ValidatorBuilder.of<KotlinUser>()
                .konstraint(KotlinUser::name) 
                    notEmpty()
                            .notNull()
                            .pattern("[a-z]*").message("Only lower case first name")
                
                .forEach(KotlinUser::phones, 
                    constraint(String::toString, "value") 
                        it.notNull().notEmpty().pattern("\\d10").message("Only 10 digits")
                    
                )
                .build()
    

Here 是您的代码的 YAVI 版本。

【讨论】:

感谢您的回答。但是,如果我必须编写代码来进行验证,我为什么要使用库呢?我最终编写了自己的验证器,使用 @field 的组合用于简单类型和标准 kotlin forEach 用于集合。【参考方案4】:

目前不支持此功能。 Kotlin 编译器目前忽略类型上的注解。

查看详情:

https://discuss.kotlinlang.org/t/i-have-a-question-about-applying-bean-validation-2-0/5394 Kotlin data class and bean validation with container element constraints

在 Kotlin 问题跟踪器上也有问题:

https://youtrack.jetbrains.net/issue/KT-26605 https://youtrack.jetbrains.net/issue/KT-13228

后者有目标版本1.4

【讨论】:

从 1.3.70 版本开始支持:blog.jetbrains.com/kotlin/2020/03/kotlin-1-3-70-released

以上是关于我们如何在 Collection 上进行 kotlin 数据类验证?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Joomla 中使用源代码控制,同时允许用户继续在生产服务器上进行内容更改?

如何在远程 Java Web 应用程序上进行内存分析

JDK源码Collections

在pdf上进行修改文字,PDF文字修改方法

如何在 Linux,MacOS 及 Windows 上进行安装 Elasticsearch

你如何在 SSE2 上进行带符号的 32 位扩展乘法?