如何在使用反应式数据源的 WebFlux 上编写自定义验证器

San*_*jay 6 spring spring-data-mongodb project-reactor spring-webflux

在 Spring MVC 中,我有一个@UniqueEmail自定义的休眠验证器(用于在注册时检查电子邮件的唯一性),如下所示:

public class UniqueEmailValidator
implements ConstraintValidator<UniqueEmail, String> {

    private UserRepository userRepository;

    public UniqueEmailValidator(UserRepository userRepository) {

        this.userRepository = userRepository;
    }

    @Override
    public boolean isValid(String email, ConstraintValidatorContext context) {

        return !userRepository.findByEmail(email).isPresent();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我正在使用反应式 MongoDB 迁移到 WebFlux,代码如下:

public class UniqueEmailValidator
implements ConstraintValidator<UniqueEmail, String> {

    private MongoUserRepository userRepository;

    public UniqueEmailValidator(MongoUserRepository userRepository) {

        this.userRepository = userRepository;
    }

    @Override
    public boolean isValid(String email, ConstraintValidatorContext context) {

        return userRepository.findByEmail(email).block() == null;
    }
}
Run Code Online (Sandbox Code Playgroud)

首先,block像上面那样使用看起来不太好。其次,它不起作用,这是错误:

Caused by: java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3
Run Code Online (Sandbox Code Playgroud)

这件事该怎么办呢?我当然可以使用 MongoTemplate 阻塞方法,但是有没有办法反应性地处理这个问题?我可以在服务方法中手动执行此操作,但我希望将此错误与其他错误(例如“短”密码)一起显示给用户。

Bri*_*zel 3

Scheduler从 Reactor 3.2.0 开始,禁止在并行或单个内部使用阻塞 API,并会引发您所看到的异常。所以,当你说它看起来不太好时,你说得对——它不仅对你的应用程序真的很糟糕(它可能会阻止新请求的处理并使整个事情崩溃),而且它太糟糕了,以至于 Reactor 团队决定将其视为错误。

现在的问题是您想在调用中执行一些与 I/O 相关的工作isValid。该方法的完整签名是:

boolean isValid(T value, ConstraintValidatorContext context)
Run Code Online (Sandbox Code Playgroud)

签名表明它是阻塞的(它不返回反应类型,也不提供结果作为回调)。所以你不能在那里做 I/O 相关的或涉及延迟的工作。在这里,您想要根据数据库检查一个条目,该条目恰好属于该类别。

我认为您不能将其作为此验证合同的一部分,而且我不知道有任何替代方案。