reactive-programming - 使用项目 react 器将验证包括到 react 链中

标签 reactive-programming spring-webflux project-reactor

我想组成一个 react 器链,基本上会做如下事情:

  1. 验证提交的 User 属性,例如 firstName 的长度、lastNameemail 的有效性.我会使用下面的验证器。
  2. 验证提交的 email 没有被其他人使用。为此,我会使用响应式(Reactive)存储库。
  3. 如果以上所有验证检查均通过,则保存用户

用户:

@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @Id
    private Integer id;
    private String firstName;
    private String lastName;
    private String email;
}

响应式存储库:

public interface UserRepository extends ReactiveCrudRepository<User, Long> {

    @Query("select id, first_name, last_name, email from user u where u.email = :email")
    Mono<User> findByEmail(String email);
}

验证器:

@Component
public class UserValidator implements Validator {

    private final static String EMAIL = "email";
    private static final String FIRST_NAME = "firstName";
    private static final String LAST_NAME = "lastName";

    @Override
    public boolean supports(Class<?> clazz) {
        return User.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        User user = (User) target;

        rejectIfEmptyOrWhitespace(errors, EMAIL, "email.required");
        rejectIfEmptyOrWhitespace(errors, FIRST_NAME, "firstName.required");
        rejectIfEmptyOrWhitespace(errors, LAST_NAME, "lastName.required");

        validateFirstName(errors, user.getFirstName());
        validateLastName(errors, user.getLastName());
        validateEmail(errors, user.getEmail());
    }

    private void validateEmail(Errors errors, String email) {
        EmailValidator emailValidator = EmailValidator.getInstance();
        if (!emailValidator.isValid(email)) {
            errors.rejectValue(EMAIL, "email.invalid");
        }
    }

    private void validateFirstName(Errors errors, String firstName) {
        if (firstName.length() < 2) {
            errors.rejectValue(FIRST_NAME, "firstName.min");
        }
    }

    private void validateLastName(Errors errors, String lastName) {
        if (lastName.length() < 2) {
            errors.rejectValue(LAST_NAME, "lastName.min");
        }
    }
}

处理方法:

public Mono<ServerResponse> saveUser(ServerRequest serverRequest) {
    return serverRequest.bodyToMono(User.class)
        // Use validator here
        .flatMap(this::createUserIfEmailNotExists);
}

辅助方法:

private Mono<ServerResponse> createUserIfEmailNotExists(User user) {
    return userRepository.findByEmail(user.getEmail())
        .flatMap(existingUser ->
            status(BAD_REQUEST).contentType(APPLICATION_JSON)
                .body(BodyInserters.fromObject("User already exists."))
        )
        .switchIfEmpty(
            userRepository.save(user)
                .flatMap(newUser -> status(CREATED).contentType(APPLICATION_JSON)
                    .body(BodyInserters.fromObject(newUser)))
        );
}

我不确定如何从被动的角度来实现这一点。理想情况下, react 链中将有 3 个步骤映射到上述各点。

这是我尝试过的方法,但我在处理方法参数和返回类型时遇到了问题,这会破坏序列流...

private Mono<ServerResponse> validateUser(User user) {
    Errors errors = new BeanPropertyBindingResult(user, User.class.getName());
    userValidator.validate(user, errors);
    if (errors.hasErrors()) {
        return status(BAD_REQUEST).contentType(APPLICATION_JSON)
            .body(BodyInserters.fromObject(errors.getAllErrors()));
    } else {
        return Mono.empty();
    }
}

有人可以帮忙吗?

最佳答案

这样怎么样?

private Mono<ServerResponse> validateUser(User user) {
    return Mono.just(new BeanPropertyBindingResult(user, User.class.getName()))
        .doOnNext(err -> userValidator.validate(user, err))
        .filter(AbstractBindingResult::hasErrors)
        .flatMap(err ->
            status(BAD_REQUEST)
                .contentType(APPLICATION_JSON)
                .body(BodyInserters.fromObject(err.getAllErrors()))
        );
}

private Mono<ServerResponse> createUserIfEmailNotExists(User user) {
    return userRepository.findByEmail(user.getEmail())
        .flatMap(existingUser ->
            status(BAD_REQUEST).contentType(APPLICATION_JSON)
                .body(BodyInserters.fromObject("User already exists."))
        )
        .switchIfEmpty(
            validateUser(user)
                .switchIfEmpty(
                    userRepository.save(user)
                        .flatMap(newUser -> status(CREATED).contentType(APPLICATION_JSON)
                            .body(BodyInserters.fromObject(newUser)))
                )
        );
}

关于reactive-programming - 使用项目 react 器将验证包括到 react 链中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57637576/

相关文章:

c# - 我们如何在 IObservable<T> 上使用组函数,同时观察未完成或在热的、长时间运行的可观察对象上

javascript - Rx.Observable.groupBy 会清理空流吗?

spring-boot - Spring Webflux 抛出 "block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-2"

spring - 如何在 Spring Reactor Web 应用程序中执行一系列操作并确保一个操作在下一个操作之前完成?

spring-webflux - 在 webflux 中读取请求正文

project-reactor - Spring Mongo 响应式(Reactive)处理数据库保存错误

java - 将 Observable 转为 ObservableValue/Binding/EventStream 的最有效方法?

c# - .Net 5.0 中对 WPF 的响应式扩展支持

java - Reactive Spring 不支持 ServerHttpRequest 作为 REST 端点测试中的参数?

java - Spring Boot Webflux - flatMap 是链接 http 调用的正确方法吗?