title: Including validation into a reactive chain with project reactor
body: <p>I would like to compose a reactor chain that would basically do as follows:</p>
  
  <ol>
  <li>Validate a submitted <code>User</code> properties such as the length of the <code>firstName</code>, <code>lastName</code> or validity of the <code>email</code>. I would use the validator below.</li>
  <li>Validate that the submitted <code>email</code> is not already used by someone else. I would use a reactive repository for that purpose.</li>
  <li>Save the <code>User</code> if all above validation checks pass.</li>
  </ol>
  
  <p>User:</p>
  
  <pre><code>@Builder
  @Data
  @AllArgsConstructor
  @NoArgsConstructor
  public class User {
  
      @Id
      private Integer id;
      private String firstName;
      private String lastName;
      private String email;
  }
  </code></pre>
  
  <p>Reactive repository:</p>
  
  <pre><code>public interface UserRepository extends ReactiveCrudRepository&lt;User, Long&gt; {
  
      @Query("select id, first_name, last_name, email from user u where u.email = :email")
      Mono&lt;User&gt; findByEmail(String email);
  }
  </code></pre>
  
  <p>Validator:</p>
  
  <pre><code>@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&lt;?&gt; 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() &lt; 2) {
              errors.rejectValue(FIRST_NAME, "firstName.min");
          }
      }
  
      private void validateLastName(Errors errors, String lastName) {
          if (lastName.length() &lt; 2) {
              errors.rejectValue(LAST_NAME, "lastName.min");
          }
      }
  }
  </code></pre>
  
  <p>Handler method:</p>
  
  <pre><code>public Mono&lt;ServerResponse&gt; saveUser(ServerRequest serverRequest) {
      return serverRequest.bodyToMono(User.class)
          // Use validator here
          .flatMap(this::createUserIfEmailNotExists);
  }
  </code></pre>
  
  <p>Helper method:</p>
  
  <pre><code>private Mono&lt;ServerResponse&gt; createUserIfEmailNotExists(User user) {
      return userRepository.findByEmail(user.getEmail())
          .flatMap(existingUser -&gt;
              status(BAD_REQUEST).contentType(APPLICATION_JSON)
                  .body(BodyInserters.fromObject("User already exists."))
          )
          .switchIfEmpty(
              userRepository.save(user)
                  .flatMap(newUser -&gt; status(CREATED).contentType(APPLICATION_JSON)
                      .body(BodyInserters.fromObject(newUser)))
          );
  }
  </code></pre>
  
  <p>I am not sure how to go about achieving this from a reactive point of view. Ideally there would be 3 steps in the reactive chain mapping to the points above.</p>
  
  <p>Here is what I have tried but I am having trouble with the method arguments and return types breaking the flow of the sequence...</p>
  
  <pre><code>private Mono&lt;ServerResponse&gt; 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();
      }
  }
  </code></pre>
  
  <p>Can someone please help?</p>