10

I am trying to follow the best practise of autowiring Webclient using WebClient Builder but little confused.

Here is my Main Application in which i am producing a Webclient Builder and autowiring it in one of my service class

 @SpringBootApplication public class MyApplication { @Bean public WebClient.Builder getWebClientBuilder() { return WebClient.builder(); } public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); }} ServiceImpl Class public class MyServiceImpl implements MyService { private static final String API_MIME_TYPE = "application/json"; private static final String API_BASE_URL = "http://localhost:8080"; private static final String USER_AGENT = "Spring 5 WebClient"; private static final Logger logger = LoggerFactory.getLogger(MyServiceImpl.class); @Autowired private WebClient.Builder webClientBuilder; @Override public Mono<Issue> createIssue(Fields field) { return webClientBuilder.build() .post() .uri("/rest/api/") .body(Mono.just(field), Fields.class) .retrieve() .bodyToMono(Issue.class); }} 

I am trying to build the webClientBuilder with BaseURl, DefaultHeader etc. I tried to initialize it inside MyServiceImpl Constructer but not sure if its correct or not.

public MyServiceImpl() { this.webClientBuilder .baseUrl(API_BASE_URL).defaultHeader(HttpHeaders.CONTENT_TYPE, API_MIME_TYPE) .defaultHeader(HttpHeaders.USER_AGENT, USER_AGENT) .build(); } 

Am i doing it correct or is there a better way to do it.

Currently I have 2 ServiceImpls to call Different Apis and thats the reason i tried to set the 'baseurl' and other defaults in service itself.

Please Help. TIA

1 Answer 1

19

Usually, your approach would be something like this:

@SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } @Configuration public class MyApplicationConfiguration { @Bean public WebClient myWebClient(WebClient.Builder webClientBuilder) { return webClientBuilder .baseUrl(API_BASE_URL) .defaultHeader(HttpHeaders.CONTENT_TYPE, API_MIME_TYPE) .defaultHeader(HttpHeaders.USER_AGENT, USER_AGENT) .build(); } } @Service public class MySericeImpl implements MyService { @Autowired private WebClient myWebClient; @Override public Mono<Issue> createIssue(Fields field) { return myWebClient .post() .uri("/rest/api/") .body(Mono.just(field), Fields.class) .retrieve() .bodyToMono(Issue.class); } } 

The key thing to remember is that WebClient.Builder is already pre-configured for you and Bean is already created. So you just need to autowire it, adjust the configuration and build final WebClient.

It is also possible to use another approach to configure it. There are 3 main approaches to customize WebClient. See official docs for more details https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-webclient.


Edit for consuming more APIs - configure multiple WebClients and autowire them in an appropriate service class.

@Configuration public class MyApplicationConfiguration { @Bean public WebClient myWebClientForApi1(WebClient.Builder webClientBuilder) { return webClientBuilder .clone() .baseUrl(API_1_BASE_URL) .defaultHeader(HttpHeaders.CONTENT_TYPE, API_MIME_TYPE) .defaultHeader(HttpHeaders.USER_AGENT, USER_AGENT) .build(); } @Bean public WebClient myWebClientForApi2(WebClient.Builder webClientBuilder) { return webClientBuilder .clone() .baseUrl(API_2_BASE_URL) .defaultHeader(HttpHeaders.CONTENT_TYPE, API_MIME_TYPE) .build(); } } 
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks @raestio(Updated my question) - Actually i have 2 ServiceImpls to call 2 different RestApis so i tried initialising baseurl in serviceimpl itself. Currently i am trying Monolithic way with both the services in a same app. The next is i want to try it as microservice thn this approach will fine.
No problem, I've updated the answer as well. You can use 'clone()' method and configure multiple WebClients without changing the other one. Then you can just autowire the one you want in a service class with '@Qualifier'. There is no need to create WebClient in the service classes. It's the configuration, so it's good to have it in @Configuration classes. It's more clear and you are closer to Single Responsibility Principle.
Yes, the approach would be to set them in the place where you are creating WebClients, which is Config class - MyApplicationConfiguration. So you can pick the properties with @Value annotation. On the side note, usually you don't want to have a password directly included. Even if it's encoded, it's not encrypted so you can still see the password value. Which you don't want unless it's just some personal project and not for some company where everyone would be able to see it. You can either encrypt it or if develop the app for Cloud and use e.g. Kubernetes, you can use Secrets.
Thanks man, Yes indeed i am going to use Kubernetes in near future and i currently preparing for CKAD also :) . I have worked with HTTPClient Library and Rest Template but I am very new to this Webclient part and struggling a lot. Actually i am working on Servicenow APIs like get,create,update. i have few questions which i am planning will post in Stackover flow this week. will paste it here in our conversation
Happy to help :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.