Spring 5 Webflux 功能端点 - 如何执行输入验证?

问题描述 投票:0回答:4

根据当前文档(5.0.0.RELEASE),Spring Webflux 在使用带注释的控制器时支持验证:

默认情况下,如果 Bean Validation 存在于类路径中 — 例如 Hibernate Validator,LocalValidatorFactoryBean 被注册为 全局验证器与 @Valid 一起使用并在 @Controller 上进行验证 方法参数。

但是没有提及如何使用功能端点实现自动化。事实上,文档中唯一的输入处理示例并不能验证任何内容:

public Mono<ServerResponse> createPerson(ServerRequest request) { 
    Mono<Person> person = request.bodyToMono(Person.class);
    return ServerResponse.ok().build(repository.savePerson(person));
}

我们应该手动执行此操作还是有某种自动方法可以执行此操作?

spring spring-mvc hibernate-validator spring-webflux
4个回答
23
投票

在 Spring 5.0 版本中,没有自动方法在功能端点中进行验证,因此必须手动完成此类验证。

虽然目前没有具体计划这样做,但我们将来可能会添加某种验证。但即便如此,它也将是一个显式的方法调用,而不是一个自动机制。总体而言,功能端点模型的设计比基于注释的模型更加明确。


7
投票

正如 arjen-poutsma 所说,似乎无法在 Spring 5 功能端点上运行自动验证。

Spring 文档对此不是很清楚,并且没有建议任何方法。

这篇 Baeldung 文章中,您将找到有关如何使用此方法运行验证的想法(免责声明:我是本文的作者:))

简而言之,您可以按照以下步骤操作:

  1. 实施 Spring 验证器来评估您的资源
  2. 创建一个抽象类,其中包含任何处理程序在处理请求时都会遵循的基本过程,让子类在数据有效时做什么
  3. 使您的请求处理程序类扩展此抽象类,实现此抽象方法,说明它将期望的主体,以及需要使用什么验证器来验证它

编辑:

我一直在关注这个相关的 Spring 问题,看来我们现在依赖于有关此主题的官方文档:https://docs.spring.io/spring-framework/reference/web/webflux-featured.html #webflux-fn-handler-validation

建议的方法是使用文章中解释的验证器:

验证

功能端点可以使用 Spring 的验证工具将验证应用于请求正文。


0
投票

在当前版本(2.0.4.RELEASE)中,没有办法使用句柄进行自动验证,但是您始终可以像这样进行手动验证:

@Slf4j
@Component
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@RequiredArgsConstructor
public class MyHandlerValidator implements HandlerValidator<MyResource> {

    Validator validator;

    @Override
    public void callValidator(final MyResource fdr) {
        final DataBinder binder = new DataBinder(fdr);
        binder.setValidator(validator);
        binder.validate();

        if (binder.getBindingResult().hasErrors()) {
            final String reason = binder.getBindingResult().getFieldError().toString();
            log.error(reason);
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, reason);
        }
    }   
}

与此相关的事情是,您应该像自动验证一样抛出 WebExchangeBindException,但是我无法创建 MethodParameter 女巫是创建此异常的依赖项。

更新: Spring 向我们展示了一种方法来做到这一点,这与我的解决方案类似,但是,我认为在文档

方面还不够

0
投票

只是为了演示一些工作代码。如果您需要基于对象注释的简单验证,例如:

@Value
@Builder
@Jacksonized
public class SigninRequest {
    
    @NotBlank(message = "The username is mandatory")
    @Email(message = "The username should be valid Email")
    String username;
    
    @NotBlank(message = "The password is mandatory")
    String password;
}

在处理程序中,您只需要一个简单的附加操作符 doOnNext:

@Component
@RequiredArgsConstructor
public class AuthHandler {

    private final AuthService authService;
    
    private final ObjectValidator validator;

    public Mono<ServerResponse> signin(ServerRequest request) {

        return ok().body(
                request.bodyToMono(SigninRequest.class)
                .doOnNext(validator::validate) //<-- just one single line
                        .flatMap(login -> authService.authenticate(login.getUsername(), login.getPassword())),
                AuthResult.class);

    }

}

ObjectValidator 正在执行实际验证,并在出现验证错误时抛出运行时异常并显示 4xx 错误:

@Component
@RequiredArgsConstructor
public class ObjectValidator {

    private final Validator validator;

    public <T> T validate(T object) {
        var errors = validator.validate(object);
        if (errors.isEmpty()) {
            return object;
        } else {
            String errorDetails = errors.stream().map(er -> er.getMessage()).collect(Collectors.joining(", "));
            throw new ObjectValidationException(errorDetails);
        }
    }
}

例外:

@ResponseStatus(code = HttpStatus.UNPROCESSABLE_ENTITY)
public class ObjectValidationException extends RuntimeException {

    public ObjectValidationException(String errorDetails) {
        super("Please supply the valid data: " + errorDetails);
    }
}

如果正确设置全局错误处理,您可以保持处理程序代码干净并在所有处理程序中重用对象验证器。

© www.soinside.com 2019 - 2024. All rights reserved.