首页 > 解决方案 > 有谁知道为什么@Valid 因 javax.validation 失败而与 spring starter 验证一起使用

问题描述

我有以下控制器:

    @PostMapping( "v1/libraryEvent" )
    public ResponseEntity<LibraryEvent> postLibraryEvent( @RequestBody @Valid LibraryEvent libraryEvent )
          throws JsonProcessingException
    {
        //Does some stuff
    }

它使用此依赖项,其中包括:

import javax.validation.Valid;

LibraryEvent DTO 如下所示:

@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class LibraryEvent
{
    private Integer libraryEventId;

    private LibraryEventType libraryEventType;

    @Valid
    @NotNull
    private Book book;
}

该图书馆活动中的书如下:

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class Book
{
    @NotNull
    private Integer bookId;
    @NotNull
    private String bookName;
    @NotNull
    private String bookAuthor;
}

我也有这个测试,它使用 MockMvc 向控制器发送一个模拟请求以测试那个:

    @Test
    void postLibraryEvent_4xx() throws Exception {

        //given
        final Book book = Book.builder()
                              .bookId( null )
                              .bookAuthor( null )
                              .bookName( "Kafka using spring boot" )
                              .build();

        final LibraryEvent libraryEvent = LibraryEvent.builder()
                .book( book )
                .build();

        final String json = objectMapper.writeValueAsString( libraryEvent );

        mockMvc.perform(post("/v1/libraryEvent")
                .content(json)
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().is4xxClientError());

    }

到目前为止一切都很好,问题是在我build.gradle使用 javax.validation 依赖项时,当我使用该依赖项运行测试时,@Valid注释不起作用,因为它返回 HttpStatus.OK (200) 而不是 Bad request (400 ),应该是这样,因为这本书的 id 和作者被传递为 null。这就是我的 gradle 文件的依赖项部分是如何使用 javax 设置的:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'javax.validation:validation-api:2.0.1.Final'
    testImplementation 'org.awaitility:awaitility:4.0.3'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation ('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

我通过用spring boot starter验证替换javax依赖解决了这个问题,一旦我在更改后再次运行测试,它通过并且@Valid控制器中的工作,因为测试返回4XX HTTP状态。我的 gradle 现在看起来像这样:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    testImplementation 'org.awaitility:awaitility:4.0.3'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation ('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

但这很奇怪,因为 spring 验证应该使用与 javax 相同的验证,或者至少@Valid应该以相同的方式运行。所以我的问题是,谁能向我解释 javax 验证和 spring 验证之间的区别,以及为什么 javax 失败而不是 spring 验证?

先感谢您!

标签: javaspringspring-bootvalidation

解决方案


https://docs.spring.io/spring-framework/docs/5.3.9/reference/html/core.html#validation-beanvalidation-spring上的 Spring 文档提供了以下用于引导 Bean 验证提供程序的示例:

import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

@Configuration
public class AppConfig {

    @Bean
    public LocalValidatorFactoryBean validator() {
        return new LocalValidatorFactoryBean();
    }
}

它继续说:

Bean Validation 提供程序,例如 Hibernate Validator,预计会出现在类路径中并且会被自动检测到。

spring-boot-starter-validation 依赖似乎只做这两件事。


推荐阅读