使用Spring Reactive时如何验证Mono


问题内容

我们正在为一个项目评估Spring 5,并且不确定如何最好地验证Mono参数。传统上,我们一直使用
MethodValidationPostProcessor 来验证我们的方法参数,如下所示:

@Validated
@Service
public class FooService

@Validated(SignUpValidation.class)
public void signup(@Valid UserCommand userCommand) {

    ...
}

然后,我们将在ControllerAdvice或中处理异常ErrorController,并将适当的4xx响应传递给客户端。

但是,当我将参数更改为时Mono,如下所示,它似乎不再起作用。

@Validated
@Service
public class FooService

@Validated(SignUpValidation.class)
public Mono<Void> signup(@Valid Mono<UserCommand> userCommand) {

    ...
}

据我了解,Spring Reactive可能实际上不起作用。那么,Spring
5验证Monos和Fluxes,然后发送适当的错误响应的最佳实践是什么?


问题答案:

很快,在回答该问题之前,方法的void返回类型在反应式应用程序中非常罕见。看来,此方法应该异步执行实际工作,但该方法返回一个同步类型。我已将其更改为Mono<Void>答案。

如参考文档中所述,Spring
WebFlux确实支持验证。

但此处的最佳做法有所不同,因为方法参数可以是反应性类型。如果method参数尚未解析,则无法获得验证结果。

因此,类似的事情实际上是行不通的:

// can't have the BindingResult synchronously,
// as the userCommand hasn't been resolved yet
public Mono<Void> signup(@Valid Mono<UserCommand> userCommand, BindingResult result)

// while technically feasible, you'd have to resolve 
// the userCommand first and then look at the validation result
public Mono<Void> signup(@Valid Mono<UserCommand> userCommand, Mono<BindingResult> result)

反应式运算符更惯用且更易于使用:

public Mono<Void> signup(@Valid Mono<UserCommand> userCommand) {
    /*
     * a WebExchangeBindException will flow through the pipeline
     * in case of validation error.
     * you can use onErrorResume or other onError* operators
     * to map the given exception to a custom one
     */
    return userCommand.onErrorResume(t -> Mono.error(...)).then();
}