Is there a way to integrate java bean validation api With Spring RestTemplate
Asked Answered
M

1

7

Is there a way to integrate spring RestTemplate with JavaBean validation api. ?

I know there is a integration for spring controller. you can put @Valid on request body param and if Employee is not valid you will get MethodArgumentNotValidException exception . which you can handel in exception handler class.

  @PostMapping(value = "/add", produces = APPLICATION_JSON_VALUE)
  public ResponseEntity<String> addEmployee(
       @RequestBody @Valid Employee emp) {

    //...

  }

But what I want is similar way to validate response from spring restTemplate like when I call this way - I want to get same(or maybe other) exception from spring.

Employee emp = template.exchange("http:///someUrl", HttpMethod.GET, null);

I know I can inject validator like this and call validator.validate(..) on reponse.

  @Autowired
  private Validator validator;

But I do not want to do it each time manually.

Mckenney answered 26/7, 2017 at 17:19 Comment(2)
What is your use case that you would need to do this? Technically I can't really think of one where you wouldn't want to use the validator object, unless it is some sort of proxy service... It is okay to validate it manually and in some cases can be more useful.Castorena
thanks for reply. I just thought maybe there is some solution in spring. I have lot's of res calls and I don't want to call validate method on each object which returned from restTemplate.Mckenney
E
12

You can subclass RestTemplate and validate the response just right after it was extracted from raw data.

Example:

public class ValidatableRestTemplate extends RestTemplate {

    private final Validator validator;

    public ValidatableRestTemplate(Validator validator) {
        this.validator = validator;
    }

    @Override
    protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor) throws RestClientException {
        final T response = super.doExecute(url, method, requestCallback, responseExtractor);
        Object  body;
        if (response instanceof ResponseEntity<?>) {
            body = ((ResponseEntity) response) .getBody();
        } else {
            body = response;
        }
        final Set<ConstraintViolation<Object>> violations = validator.validate(body);
        if (violations.isEmpty()) {
            return response;
        }

        throw new ConstraintViolationException("Invalid response", violations);
    }
}

The usage then is quite simple, just define your subclass as RestTemplate bean.

Full sample:

@SpringBootApplication
public class So45333587Application {

    public static void main(String[] args) { SpringApplication.run(So45333587Application.class, args); }

    @Bean
    RestTemplate restTemplate(Validator validator) { return new ValidatableRestTemplate(validator); }

    public static class ValidatableRestTemplate extends RestTemplate {
        private final Validator validator;

        public ValidatableRestTemplate(Validator validator) { this.validator = validator; }

        @Override
        protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor) throws RestClientException {
            final T response = super.doExecute(url, method, requestCallback, responseExtractor);
            Object  body;
            if (response instanceof ResponseEntity<?>) {
                body = ((ResponseEntity) response).getBody();
            } else {
                body = response;
            }
            final Set<ConstraintViolation<Object>> violations = validator.validate(body);
            if (violations.isEmpty()) {
                return response;
            }

            throw new ConstraintViolationException("Invalid response", violations);
        }
    }

    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
    public static class Post {
        @Min(2) // change to '1' and constraint violation will disappear
        private Long   id;
        private Long   userId;
        private String title;
        private String body;

        @Override
        public String toString() { 
            return String.format("Post{id=%d, userId=%d, title='%s', body='%s'}", id, userId, title, body); 
        }
    }

    @Bean
    CommandLineRunner startup(RestTemplate restTemplate) {
        return args -> {
            final ResponseEntity<Post> entity = restTemplate.exchange("https://jsonplaceholder.typicode.com/posts/1", HttpMethod.GET, null, Post.class);
            System.out.println(entity.getBody());
        };
    }
}
Eggcup answered 27/7, 2017 at 8:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.