-
Notifications
You must be signed in to change notification settings - Fork 0
RestTemplate vs WebClient
NOTE: As of 5.0, the non-blocking, reactive org.springframework.web.reactive.client.WebClient offers a modern alternative to the RestTemplate with efficient support for both sync and async, as well as streaming scenarios. The RestTemplate will be deprecated in a future version and will not have major new features added going forward.
- https://dzone.com/articles/raw-performance-numbers-spring-boot-2-webflux-vs-s
- https://www.baeldung.com/spring-webclient-resttemplate
package com.khan.vaquar.service;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@Component
public class RestTemplateDemo {
private static final Logger log = LoggerFactory.getLogger(RestTemplateDemo.class);
@Value("${external.api.uri}") //any external apl want to call read from properties file
private String externalUrl;
@Value("${external.api.username}")//read from properties file
private String externalApiUsername;
@Value("${external.api.password}")//read from properties file
private String externalApiPassword;
@Value("${external.api.connection.timeout}")//read from properties file
private String externalApiConnectionTimeOut;
@Value("${external.api.read.timeout}")//read from properties file
private String externalApiReadTimeOut;
/**
* this public method call from outside any controller ,service ,etc
*/
public String retrieveData(String id, String name) {
HttpHeaders headers =createHeader();
String requestJson = "{\"name\":\"" + name + "\"}";
HttpEntity<String> request = new HttpEntity<String>(requestJson, headers);
// external call time
long startTime = System.currentTimeMillis();
ResponseEntity<String> response = customRestTemplate().exchange(externalUrl, HttpMethod.POST, request,
String.class);
long endTime = System.currentTimeMillis();
long duration = (endTime - startTime); // divide by 1000000 to get milliseconds.
log.info("{\"RestTemplateDemo\":{\"id\":\"" + id + "\",\"external call duration\":" + duration + "}}");
ObjectMapper mapper = new ObjectMapper();
return response.getBody();
}
@Bean
public RestTemplate customRestTemplate() {
SimpleClientHttpRequestFactory httpRequestFactory = createRequestFactory();
RestTemplate restTemplate= new RestTemplate(httpRequestFactory);
return restTemplate;
}
/**
* connection time out
* @return
*/
private SimpleClientHttpRequestFactory createRequestFactory() {
SimpleClientHttpRequestFactory _requestFactory = new SimpleClientHttpRequestFactory();
_requestFactory.setConnectTimeout(Integer.valueOf(connectionTimeOut));
_requestFactory.setReadTimeout(Integer.valueOf(readTimeOut));
return _requestFactory;
}
/**
* Create header
* @return
*/
private HttpHeaders createHeader() {
HttpHeaders headers = new HttpHeaders();
String userPass = externalApiUsername + ":" + externalApiPassword;
String authHeaderValue = "Basic " + Base64.getEncoder().encodeToString(userPass.getBytes());
headers.set(HttpHeaders.AUTHORIZATION, authHeaderValue);
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
/**
* Method using to print stack trace in logs
* @param e
* @return
*/
private String pringtStackTrace(Exception exception) {
if(null ==exception) {
return "";
}
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
String sStackTrace = sw.toString();
return sStackTrace;
}
-
https://howtoprogram.xyz/2017/10/28/reactive-http-client-spring-5-webclient/
package com.example.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import com.example.service.ContentService; import com.example.service.Respose; @RestController public class WebClientController { private ContentService contentService; WebClientController(ContentService contentService) { this.contentService = contentService; } //http://localhost:8081/employees/41391 @GetMapping("/employees/{id}") Respose getPost(@PathVariable int id) { return contentService.getPost(id); }
}
package com.example.service;
public interface ContentService { public Respose getPost(int id);
}
package com.example.service; public class Respose { private int id; private String employee_name; private String employee_salary; private int employee_age; private String profile_image; private String body; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getEmployee_name() { return employee_name; } public void setEmployee_name(String employee_name) { this.employee_name = employee_name; } public String getEmployee_salary() { return employee_salary; } public void setEmployee_salary(String employee_salary) { this.employee_salary = employee_salary; } public int getEmployee_age() { return employee_age; } public void setEmployee_age(int employee_age) { this.employee_age = employee_age; } public String getProfile_image() { return profile_image; } public void setProfile_image(String profile_image) { this.profile_image = profile_image; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } @Override public String toString() { return "Respose [id=" + id + ", employee_name=" + employee_name + ", employee_salary=" + employee_salary + ", employee_age=" + employee_age + ", profile_image=" + profile_image + ", body=" + body + "]"; } } package com.example.service; import java.util.Base64; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import org.springframework.web.reactive.function.client.WebClient; import io.netty.handler.timeout.ReadTimeoutHandler; import reactor.core.publisher.Mono; import reactor.netty.http.client.HttpClient; import reactor.netty.tcp.TcpClient; @Service public class ContentServiceImpl implements ContentService { private static final Logger LOGGER = Logger.getLogger(ContentServiceImpl.class.getName()); @Autowired private HttpServletRequest request; /** * * @return */ public WebClient webClient() { TcpClient tcpClient = TcpClient.create() .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler(30000, TimeUnit.MILLISECONDS))); return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient))).build(); } private ExchangeFilterFunction logRequest() { return (clientRequest, next) -> { LOGGER.info("Request: {} {}" + clientRequest.method() + clientRequest.url()); clientRequest.headers() .forEach((name, values) -> values.forEach(value -> LOGGER.info("{}={}" + name + value))); return next.exchange(clientRequest); }; } /** * */ @Override public Respose getPost(int id) { //return //webClient().get().uri("http://dummy.restapiexample.com/api/v1/employees") //.retrieve().bodyToMono(Respose.class); String uri ="http://dummy.restapiexample.com/api/v1/employees"; String auth[]= {"xyz","xyz@ttm"}; String req="{\n" + "\"empid\": \"1234\"\n" + "}"; //String requestHeader = this.request.getHeader(HttpHeaders.AUTHORIZATION); //System.out.println("requestHeader="+requestHeader); String responeJson=this.webClient() .get() .uri(uri) .header(HttpHeaders.AUTHORIZATION, "Basic VGVzkDpVZZN0oDEcMzQ=") // Get header from postman and set in request // .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .body(BodyInserters.fromObject(req)) .retrieve() .bodyToMono(String.class) .doOnSuccess(onSuccess -> LOGGER.info("{\"Response\":{\",\"response\":" + onSuccess + "}}")) .block(); /// /// Respose res=new Respose(); res.setBody(responeJson); return res; }
}
- https://stackoverflow.com/questions/45301220/how-to-mock-spring-webclient-in-unit-test
- https://stackoverflow.com/questions/45301220/how-to-mock-spring-webclient-in-unit-test?rq=1
- https://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/html/boot-features-testing.html
Flux is a stream which can emit 0..N elements:Flux, which will emit zero or multiple, possibly infinite, results and then completes.
- Flux fl = Flux.just("a", "b", "c");
Mono is a stream of 0..1 elements: Mono, which will complete after emitting a single result
- Mono mn = Mono.just("hello");
Note : there's no particular order when processing these items. It could be that the first number has already been printed on the console, while the third item is hasn't been multiplied by two yet.
So, If you have a Mono, which means that as soon as the ServerResponse is available, the WebFlux framework can utilize it. When there is only one ServerResponse expected, it's a Mono and not a Flux.
- https://dzone.com/articles/reactor-core-tutorial
- https://ordina-jworks.github.io/reactive/2016/12/12/Reactive-Programming-Spring-Reactor.html
- https://howtodoinjava.com/spring-webflux/spring-webflux-tutorial/