Multiple rest templates in spring boot - spring

I want to configure multiple rest template clients to access different API's. Both are having different authorization headers. I already configured one, Same way configured other rest template too, but that throws error bean 'restTemplate' defined in class path resource .class could not be registered..
#Configuration
public class RestTemplateConfig {
#Autowired
private HeaderRequestInterceptor headerRequestInterceptor;
//constructor
public RestClientConfig() {}
#Bean
public RestTemplate restTemplate( RestTemplateBuilder builder ) {
RestTemplate restTemplate = builder.build();
restTemplate.setInterceptors(Collections.singletonList(headerRequestInterceptor));
return restTemplate;
}
}
HeaderRequestInterceptor has base64 encoded authorization, so could not post that code here.
Another RestTemplate:
#Configuration
public class AnotherRestClientConfig {
#Autowired
private AnotherHeaderRequestInterceptor anotherHeaderRequestInterceptor;
#Bean
public RestTemplate restTemplate( RestTemplateBuilder builder ) {
RestTemplate restTemplate = builder.build();
restTemplate.setInterceptors(Collections.singletonList(anotherHeaderRequestInterceptor));
return restTemplate;
}
}
Could someone let me know how to configure multiple rest templates in an application.

you could use #Qualifier as mentioned by #VirtualTroll. Or create a specific client bean per api and hold the restemplate instance there.
#Component
public class ApiClient1 {
private final RestTemplate customRestTemplate;
public ApiClient1() {
this.customRestTemplate = ...
}
public void useApi() {
}
}

Related

Feign Client Custom Configuration Does Not Take Effect

I use feign in my project. I created a custom configuration but it is overridden by default configuration. Here are the steps:
SpringBootApplication.java
#SpringBootApplication
#EnableDiscoveryClient
#EnableFeignClients
public class MyApplication {}
FeignConfiguration.java
#RequiredArgsConstructor
public class FeignConfiguration{
private final MyConfigurationProperties myConfigProperties;
#Bean
public MetaDataRestClient metaDataRestClient(#Qualifier("metaDataHttpClient") okhttp3.OkHttpClient metaDataHttpClient) {
return Feign.builder()
.retryer(Retryer.NEVER_RETRY)
.client(new OkHttpClient(metaDataHttpClient))
.encoder(new JacksonEncoder(XML_MAPPER))
.decoder(new JacksonDecoder(XML_MAPPER))
.contract(new SpringMvcContract())
.logger(new Slf4jLogger(MetaDataRestClient.class))
.logLevel(Logger.Level.FULL)
.target(MetaDataRestClient.class, myConfigProperties.getMetadata().getEndpoint());
}
#Primary
#Bean(name = "metaDataHttpClient")
public okhttp3.OkHttpClient metaDataHttpClientWithProxy() {
return OkHttpUtil.createNewHttpClientBuilderWithProxy(myConfigProperties.getMetadata().getFeignClient().getConnectTimeout(),
myConfigProperties.getMetadata().getFeignClient().getReadTimeout()).build();
}
MetaDataRestClient.java
#FeignClient(name = "metaDataRestClient", url = "https://myurl.net", configuration = FeignConfiguration.class)
public interface MetaDataRestClient {
#Headers("Content-Type: text/xml")
#GetMapping("/metadata")
public EntityDescriptor getMetadaData();
}
I see that the metaDataRestClient bean is triggered on startup, but when I dig into feign library code, I see that this method is triggered twice: first time with my custom OkHttpClient, and second time with somehing called org.springframework.cloud.sleuth.instrument.web.client.feign.LazyClient. So my custom OkHttpClient is overridden by this lazy client.
Here is the related Feign library code that is being triggered twice:
FeignBuilder.java
public Builder client(Client client) {
this.client = client;
return this;
}
I do not have any feign configurationn in my application.yaml file. What could be the reason for that?
Thanks.

Unable to mock component that uses RestTemplateBuilder

I have a Springboot application that uses RestTemplate to access a third-party json api.
I have written a Component that has the RestTemplate field which is instantiated using Constructor Injection.
#Service("sample")
public class SampleService {
private RestTemplate restTemplate;
#Autowired
public SampleService(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
The service works fine.
I am trying to write test cases for the same.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(classes = {Application.class,ServiceTest.ContextConfiguration.class})
public class ServiceTest {
#InjectMocks
#Qualifier("sample")
SampleService sampleService;
#Configuration
static class ContextConfiguration {
#Bean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplateBuilder rtb = mock(RestTemplateBuilder.class);
RestTemplate restTemplate = mock(RestTemplate.class);
when(rtb.build()).thenReturn(restTemplate);
return rtb;
}
}
}
I am getting the below exception on test class startup.
org.mockito.exceptions.misusing.InjectMocksException:
Cannot instantiate #InjectMocks field named 'sampleService' of type 'class com.xxx.xxx.core.SampleService'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : Cannot invoke "org.springframework.boot.web.client.RestTemplateBuilder.build()" because "builder" is null
How to resolve this?

Spring TestRestTemplate timeout

When debugging a Spring integration test which uses TestRestTemplate (Note: NOT RestTemplate), I sometimes find the client side of the test times out if I'm stepping through breakpoints on the production code (server side).
How do I change the timeouts for Spring TestRestTemplate?
Add a new config class at the top of your test (I called it TestConfig):
#SpringBootTest(webEnvironment = ..., classes = [YourApplication, TestConfig])
then include that TestConfig class
and include a custom RestTemplateBuilder as follows
These can both be inner classes of your test class if needed
public static class TestConfig {
#Bean
public MyRestTemplateBuilder myRestTemplateBuilder() {
return new MyRestTemplateBuilder()
}
}
public static class MyRestTemplateBuilder extends RestTemplateBuilder {
MyRestTemplateBuilder(RestTemplateCustomizer... customizers) {
super(customizers)
}
#Override
ClientHttpRequestFactory buildRequestFactory() {
ClientHttpRequestFactory result = super.buildRequestFactory()
// Usually result is a: org.springframework.http.client.HttpComponentsClientHttpRequestFactory
result.setConnectTimeout(100000);
result.setReadTimeout(100000);
return result
}
}
Your timeouts will be modified as required.
Bonus:
For normal RestTemplate timeouts you can do:
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
// This code can be used to change the read timeout for testing
SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
simpleClientHttpRequestFactory.setReadTimeout(100); // millis
return restTemplate;
}

Spring Cloud Gateway pass bean to custom filter

We are attempting to use Spring Cloud Gateway to setup a microservice based architecture. Currently, we have defined a route programatically:
#ServletComponentScan
#SpringBootApplication
public class GatewayApplication {
// to be passed to and used by custom filter
#Autowired
RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
#Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("status", r -> r
.method(HttpMethod.GET)
.and()
.path("/status")
.filters(f -> f.rewritePath("/status", "/v2/status")
.filter(new AuthorizationFilter(restTemplate).apply(new Config(""))))
.uri("http://localhost:8081/"))
.build();
}
}
The above would route an incoming request /status via GET to another endpoint. We would like to apply a custom filter, which we have implemented in AuthorizationFilter. This filter, as the name implies, is another microservice which will either allow or deny an incoming request based on credentials and permissions.
Currently, the pattern we are following, which works, is to inject a Spring RestTemplate into the gateway class above, and then to pass this RestTemplate to the constructor of the filter.
However, how can this be done if we wanted to switch to using a YAML file for defining all the routes? Presumably in both cases Spring would be constructing a new filter for each incoming request. But in the case of YAML, how can we pass something in the construtor? If this cannot be done, is there any other way to inject a RestTemplate, or any other resource into a custom Spring gateway filter?
You can register your own custom GatewayFilterFactory. This allows you to provide a custom configuration, and within that configuration, you can use SpEL to reference a bean.
For example:
#Component
public class AuthenticationGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthenticationGatewayFilterFactory.Config> {
public AuthenticationGatewayFilterFactory() {
super(Config.class);
}
#Override
public GatewayFilter apply(Config config) {
// TODO: Implement
}
public static class Config {
private RestTemplate restTemplate;
// TODO: Getters + Setters
}
}
Now you can use SpEL to properly reference a RestTemplate bean:
spring:
cloud:
gateway:
routes:
- id: status
uri: http://localhost:8081/
filters:
- name: Authentication
args:
restTemplate: "#{#nameOfRestTemplateBean}"
predicates:
- Path=/status
Alternatively, you could inject a RestTemplate bean within your gateway filter. For example:
#Component
public class AuthenticationGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthenticationGatewayFilterFactory.Config> {
private RestTemplate restTemplate;
public AuthenticationGatewayFilterFactory(RestTemplate restTemplate) {
super(Config.class);
this.restTemplate = restTemplate;
}
#Override
public GatewayFilter apply(Config config) {
// TODO: Implement
}
public static class Config {
// TODO: Implement
}
}
The code/configuration necessary to do the inject is less complex, but it also makes it more difficult if you ever decide to put AuthenticationGatewayFilterFactory in a separate library, as the "consumers" of this library won't have any control over which RestTemplate is being injected.

How to Create or configure Rest Template using #Bean in Spring Boot

I want to define RestTemplate as an application bean using #Bean annotation in my configuration class in a spring boot application.
I am calling 4 rest services in different places in my application flow. Currently I am creating RestTemplate every time every request. Is there a way I can define that as application bean using #Bean and inject that using #Autowired?
Main reason for this question is I can able to define RestTemplate using #Bean but when I inject it with #Autowired I am loosing all defined interceptors (Interceptors are not getting called.)
Configuration Class
#Bean(name = "appRestClient")
public RestTemplate getRestClient() {
RestTemplate restClient = new RestTemplate(
new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add(new RestServiceLoggingInterceptor());
restClient.setInterceptors(interceptors);
return restClient;
}
Service Class
public class MyServiceClass {
#Autowired
private RestTemplate appRestClient;
public String callRestService() {
// create uri, method response objects
String restResp = appRestClient.getForObject(uri, method, response);
// do something with the restResp
// return String
}
}
It seems my Interceptors are not getting called at all with this configuration. But RestTemplate is able to make a call to the REST service and get a response.
Answer for Spring boot 2.*.* version.
I am using Spring boot 2.1.2.RELEASE and I also added RestTemplate in my project in a class where mail method exists.
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.setConnectTimeout(Duration.ofMillis(300000))
.setReadTimeout(Duration.ofMillis(300000)).build();
}
and Used in my service or other classes like this
#Autowired
RestTemplate res;
and in methods
HttpEntity<String> entity = new HttpEntity<>(str, headers);
return res.exchange(url, HttpMethod.POST, entity, Object.class);
Judging form the name of the interceptor, I'm guessing you're doing some logging in it? You could of missed logging level configuration. I created a small application to check weather your configuration works, using 1.3.6.RELEASE version.
In this class I define the RestTemplate bean and the interceptor with logging.
package com.example;
// imports...
#SpringBootApplication
public class TestApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(TestApplication.class);
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
#Bean(name = "appRestClient")
public RestTemplate getRestClient() {
RestTemplate restClient = new RestTemplate(
new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
// Add one interceptor like in your example, except using anonymous class.
restClient.setInterceptors(Collections.singletonList((request, body, execution) -> {
LOGGER.debug("Intercepting...");
return execution.execute(request, body);
}));
return restClient;
}
}
For logging to work, I also have to set the correct debug level in application.properties.
logging.level.com.example=DEBUG
Then I create a service where I inject this RestTemplate.
#Service
public class SomeService {
private final RestTemplate appRestClient;
#Autowired
public SomeService(#Qualifier("appRestClient") RestTemplate appRestClient) {
this.appRestClient = appRestClient;
}
public String callRestService() {
return appRestClient.getForObject("http://localhost:8080", String.class);
}
}
And also an endpoint to test this out.
#RestController
public class SomeController {
private final SomeService service;
#Autowired
public SomeController(SomeService service) {
this.service = service;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public String testEndpoint() {
return "hello!";
}
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
return service.callRestService();
}
}
By performing a GET request to http://localhost:8080/test I should expect to get the String hello! getting printed (the service makes a call to http://localhost:8080 which returns hello! and sends this back to me). The interceptor with logger also prints out Intercepting... in the console.
Edd's solution won't work if you're using Spring Boot 1.4.0 or later. You will have to use RestTemplateBuilder to get this working. Here is the example
#Bean(name="simpleRestTemplate")
#Primary
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
RestTemplate template = restTemplateBuilder.requestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()))
.interceptors(logRestRequestInterceptor) //This is your custom interceptor bean
.messageConverters(new MappingJackson2HttpMessageConverter())
.build();
return template;
}
Now you can autowire the bean into your service class
#Autowired
#Qualifier("simpleRestTemplate")
private RestTemplate simpleRestTemplate;
Hope this helps

Resources