【问题标题】:Spring Validation not working on Service Layer levelSpring Validation 在服务层级别不起作用
【发布时间】:2020-08-20 10:47:22
【问题描述】:

我有一个 Spring Boot 项目 ( 2.3.3 ),我想在其中验证服务层方法的输入参数。所以在我的 pom.xml 我添加了

        <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

因为它不再是父级的一部分。接下来我有我的服务方法接口和实现服务方法。我的实施服务用 @Validated 注释,我的方法看起来像

public void deleteGreetingById(@NotNull(message = "greetingId must not be null.")Integer greetingId) {

我还了解到,默认情况下验证仅绑定到控制器层。因此,为了也为服务层启用它,我添加了一个 PostValidationProcesser。

@Configuration
public class MethodValidationConfig {

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

当我现在使用 null 作为输入参数执行测试时,什么都没有发生,也没有抛出异常。当我这样做时

 Assert.notNull(greetingId,"greetingId must not be null"); 

在方法内部,如预期的那样抛出 InvalidParameterException。但我更喜欢基于注释的验证,因为整个类对象的@Valid 验证作为输入参数。

能否解释一下为什么没有触发验证?

编辑:

@RestController
public class GreetingsConsumerController {

    private final GreetingsService greetingsService;

    public GreetingsConsumerController(GreetingsService greetingsService) {
        this.greetingsService = greetingsService;
    }

    @PostMapping(value = "/greetings", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Greeting createGreeting( @RequestBody @Valid GreetingDto greetingDto){
        return greetingsService.addGreeting(greetingDto);
    }

    @GetMapping(value = "/greetings/{id}")
    public Greeting getGreetingById(@PathVariable Integer id){
        return greetingsService.findGreetingById(id);
    }

    @GetMapping(value = "/greetings")
    public List<Greeting> getAllGreetings(){
        return greetingsService.findAllGreetings();
    }

    @DeleteMapping(value = "/greetings/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteGreetingById(@PathVariable Integer id){

        greetingsService.deleteGreetingById(id);
    }
}

界面:

public interface GreetingsService {
    
        Greeting findGreetingById(Integer greetingId);
        List<Greeting> findAllGreetings();
        Greeting addGreeting( GreetingDto greetingDto);
        void deleteGreetingById( Integer greetingId);
    
    }

IterfaceImpl:

@Service
@Validated
public class GreetingsServiceImpl implements GreetingsService {

.
.
.
       @Override
        public void deleteGreetingById(@NotNull(message = "greetingId must not be null. ") Integer greetingId) {
            ...
        }

}

我还将 Bean 添加到我的 SpringBootApplication 但仍然没有抛出异常。

@SpringBootApplication
public class GreetingsConsumerApplication {

    public static void main(String[] args) {

        SpringApplication.run(GreetingsConsumerApplication.class, args
        );
    }
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }

}

【问题讨论】:

  • 如何调用方法?在添加 NotNull 约束的地方,该类看起来如何?
  • 我像所有通用方法一样调用该方法。我的示例中的 NotNull 与单个参数相关(您可以在我的示例中看到参数是 greetingId)。对于控制器输入 Dto,我的类约束验证按预期工作。当我尝试验证服务层中的参数时,我只是不工作。
  • 你正在使用 Spring Boot 这个MethodValidationConfig 是 Spring Boot 提取的吗?你怎么称呼那个deleteGreetingById 方法?如果在同一个类中调用它,它将不起作用。
  • @M.Deinum 我有一个接收请求的休息端点。然后我提取 id 并通过接口实例调用方法。我实际上不知道这个 MethodvalidationConfig 是否被选中。我会尝试设置断点。我从这里的示例中得到了这个解决方案baeldung.com/javax-validation-method-constraints
  • 它需要在 Spring Boot 覆盖的包中,否则您需要导入它(或者只需将 @Bean 方法添加到您的 @SpringBootApplication 注释类,这可能更容易)。跨度>

标签: java spring spring-boot validation


【解决方案1】:

以下是在服务层验证模型的示例。

class   TestModel{
    @NotNull
    private String name;
    }

TestModel model= new TestModel();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<TestModel>> violations = validator.validate(model);
        
        

【讨论】:

  • 当请求到达控制器时,我的模型验证工作。为什么有必要创建一个单独的验证器?我认为它应该像在控制器级别一样工作?
  • 据我所知,它在服务层不起作用,我们需要为此编写单独的验证器。
【解决方案2】:

我“解决”了这个问题。我的错误是我错误地配置了我的测试。我用

配置了测试

@Extendwith(SpringExtension.class)

因为我之前只编写了单元测试而不使用此类中的上下文。显然,以这种方式使用参数验证,您必须使用使整个场景成为集成测试的 Context。我很高兴它现在可以工作,对于不必要的讨论我深表歉意。我也应该在代码中发布我的测试。

虽然我很高兴它现在可以工作,但我也有点困惑。一般来说,我不想仅仅为了约束验证而启动 Spring 上下文。但这是另一个问题。

【讨论】:

    【解决方案3】:

    当你有实现接口的服务并且你引用接口时,你需要在接口上的验证注解,而不是实现类。向 GreetingsService 接口添加验证注解。

    【讨论】:

    • 我在界面和两者上都添加了它。什么都没有发生
    • 你是不是也给接口加了@Validated注解?
    • 是的,我在接口、服务和两者上都试过了。
    • 对于我的 Boot 2.3 应用程序,我不需要像您那样创建 MethodValidationPostProcessor bean,并且方法验证有效。对于直接的 Spring 应用程序,我创建了方法验证后处理器并将其验证器工厂设置为 LocalValidatorFactoryBean。
    • 我在没有这个处理器的情况下尝试过,但没有任何改变。该方法仍然使用空对象执行,没有抛出异常
    猜你喜欢
    • 2020-08-05
    • 2021-07-30
    • 1970-01-01
    • 2013-09-16
    • 1970-01-01
    • 2020-02-23
    • 2013-01-15
    • 1970-01-01
    相关资源
    最近更新 更多