在 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();
}
}
现在我正在使用反应式 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;
}
}
首先,像上面那样使用
block
看起来不太好。其次,它不起作用,这是错误:
Caused by: java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3
这该怎么办?我当然可以使用 MongoTemplate 阻塞方法,但是有没有办法反应性地处理这个问题?我可以在服务方法中手动执行此操作,但我希望将此错误与其他错误一起显示给用户(例如“短”密码)。
Scheduler
中使用阻塞 API,并抛出您所看到的异常。所以,当你说它看起来不太好时,你说得对——它不仅对你的应用程序真的很糟糕(它可能会阻止新请求的处理并使整个事情崩溃),而且它太糟糕了,以至于 Reactor 团队决定将其视为错误。
现在的问题是您想在 isValid
调用中执行一些 I/O 相关工作。该方法的完整签名是:
boolean isValid(T value, ConstraintValidatorContext context)
签名表明它是阻塞的(它不返回反应类型,也不提供结果作为回调)。所以你不能在那里做 I/O 相关的或涉及延迟的工作。在这里,您想要检查数据库中的条目,该条目恰好属于该类别。
我认为您不能将其作为此验证合同的一部分,并且我不知道有任何替代方案。
private Mono<Folder> validate(Folder folder, UserReference user) {
// init
var errors = new SimpleErrors(folder);
// validate annotations
this.validator.validate(folder, errors);
if (errors.hasErrors()) {
if (errors.getFieldError() != null) {
return Mono.error(new BadRequestException(
errors.getFieldError().getDefaultMessage()));
}
if (errors.getGlobalError() != null) {
return Mono.error(new BadRequestException(
errors.getGlobalError().getDefaultMessage()));
}
return Mono.error(new BadRequestException("Invalid folder"));
}
// validate parent
if (folder.getParentId() != null) {
return repository.findById(folder.getParentId())
.switchIfEmpty(
Mono.error(new BadRequestException("Parent folder not found.")))
.flatMap(parent -> {
var access = AccessControlList.AccessType.WRITE;
if (!parent.getAcl().hasAccess(user.getId(), access)) {
return Mono.error(new BadRequestException(
"User does not have write access to parent folder."));
}
return Mono.just(folder);
});
}
// done
return Mono.just(folder);
}