Search code examples
javaspringspring-bootreactive-programming

How to validate/process the Spring WebClient response?


So the app flow is like:

  1. Spring app receives the request -> {"id": "UUID"}
  2. Calls the external web service using the WebClient
  3. Validate the response received in step 2 from WebClient. Return true or false if validation passes.
  4. Do other validations operations(via calling other processes or ext services) and send back the response to the user.

This is the webclient which I am using(replica):

public Mono<Details> someRestCall(String name) {
        return this.webClient.get().url("/{id}/details", name)
                        .retrieve().bodyToMono(Details.class);
    }

Now I don't want to return this Mono object directly to the client(like the Angular app) via the controller, since this is an intermediary step. I want to run few validations on the response received from the WebClient.

I have tried the .block() method to retrieve the function but it seems to be a bad practice as per the reactive programming. (blocking op) Also, I am unable to understand how to use the .subscribe() method to retrieve the response object and run validation/checks on it and return True if the validation passes.

In simple terms, instead of returning a Mono object from my module/validation code, I want to return a normal boolean value/Java object.

I am new to reactive programming, can anyone please help me get this resolved?


Solution

  • With a little effort you can use JSR-303 annotation based validation on the POJOs that are returned by a WebClient call. Here's a minimal example:

    @Component
    public class MyClass {
    
      final Validator validator;
    
      MyClass(Validator validator) {
        this.validator = validator;
      }
    
      <T> T validate(T obj) {
    
        Set<ConstraintViolation<T>> violations = this.validator.validate(obj);
        if (violations.size() > 0) {
          throw new ResponseStatusException(INTERNAL_SERVER_ERROR, violations.toString());
        }
    
        return obj;
      }
    
      public Mono<Details> someRestCall(String name) {
        return this.webClient.get()
            .url("/{id}/details", name)
            .retrieve()
            .bodyToMono(Details.class)
            .doOnNext(this::validate);
      }
    }
    

    In practice you might want a better formatted violations string and if validation is done in a few places then you could lift that logic into its own class.