Search code examples
javaspring-bootresttemplate

Spring Boot starter - RestTemplate


I'm currently trying to write a Spring Boot starter that would automatically authenticate our microservices with an API gateway and include the access token in the headers for all outgoing requests (towards the gateway).
I'm creating a RestTemplate bean and giving it our custom interceptor, but my problem is, by doing this, I prevent other teams (who would be using this starter) to use their own RestTemplate config, since they would have to define the same bean leading to multiple beans existing.

@Bean
public RestTemplate restTemplate(){
  RestTemplate restTemplate = new RestTemplate();
  List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
  if (interceptors.isEmpty()){
    interceptors = new ArrayList<>();
  }
  interceptors.add(clientCredentialsAuthInterceptor());
  restTemplate.setInterceptors(interceptors);
  return restTemplate;
}

Is there another way of intercepting all outgoing requests or make the RestTemplate further configurable?


Solution

  • Not tested but it may give you a start point:

    // Create an interface that users of your dependency 
    // can implement which provides interceptors
    public interface RestTemplateAuthInterceptorProvider {
    
      // This interface provides interceptors, so they can add as many as they want
      List<ClientHttpRequestInterceptor> provideInterceptor();
    }
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    // define a conditional default implementation of your interceptor provider
    @Bean
    @ConditionalOnMissingBean(RestTemplateAuthInterceptorProvider.class)
    public RestTemplateAuthInterceptorProvider restTemplateAuthInterceptorProvider() {
      return () -> ... // implement your auth interceptor and return
    }
    
    // In your actual rest template creation use method argument injection
    // If other teams implement the RestTemplateAuthInterceptorProvider interface
    // conditional case above will be false and your implementation will not interfere
    // If they dont implement RestTemplateAuthInterceptorProvider
    // your default implementation will be here
    @Bean
    public RestTemplate restTemplate(RestTemplateAuthInterceptorProvider provider) {
      RestTemplate restTemplate = new RestTemplate();
      List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
      if (interceptors == null){
        interceptors = new ArrayList<>();
      }
      interceptors.addAll(provider.provideInterceptor()); // from argument 
      restTemplate.setInterceptors(interceptors);
      return restTemplate;
    }
    

    Edit: Another hacky approach is to manipulate already defined RestTemplate beans

    @Component
    @ConditionalOnBean(RestTemplate.class)
    public class RestTemplateBeanCustomizer {
    
      private List<RestTemplate> restTemplateBeans;
    
      // This injects all restTemplate bean definitions to your bean as a list
      @Autowired
      public RestTemplateBeanCustomizer(List<RestTemplate> restTemplateBeans) {
        this.restTemplateBeans = restTemplateBeans;
      }
    
      @PostConstruct
      public void customizeRestTemplateBeans() {
          for (RestTemplate restTemplate : restTemplateBeans) {
            // Add your interceptors message handlers etc
            // restTemplate.set...
          }
      }
    }