Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jabrena/spring-boot-http-client-review
A review of different ways to communicate with a REST endpoint
https://github.com/jabrena/spring-boot-http-client-review
Last synced: 12 days ago
JSON representation
A review of different ways to communicate with a REST endpoint
- Host: GitHub
- URL: https://github.com/jabrena/spring-boot-http-client-review
- Owner: jabrena
- License: apache-2.0
- Created: 2023-02-11T15:20:17.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2024-02-29T10:49:31.000Z (9 months ago)
- Last Synced: 2024-10-11T03:14:45.517Z (about 1 month ago)
- Language: Java
- Size: 113 KB
- Stars: 0
- Watchers: 2
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# spring-boot-http-client-poc
[![CI Builds](https://github.com/jabrena/spring-boot-http-client-poc/actions/workflows/build.yaml/badge.svg)](https://github.com/jabrena/spring-boot-http-client-poc/actions/workflows/build.yaml)
**Cloud IDEs:**
[![](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/jabrena/spring-boot-http-client-poc)
---
## How to build in local
```bash
mvn clean verify
mvn clean test -pl servlet/
```## Motivation
Spring Frameworks provide a set of Clients to interact with HTTP Protocol.
This repository tries to explore how to use the different HTTP Clients provided by the Spring Ecosystem.- https://docs.spring.io/spring-framework/reference/integration/rest-clients.html
## History
In Java, you can interact with http using the classic Object URLConnection:
```java
public List getGods() {List responseBody = new ArrayList<>();
try {
URL url = new URL(address);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-Type", "application/json");
int responseCode = connection.getResponseCode();if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder responseBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
responseBuilder.append(line);
}
reader.close();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBuilder.toString());
if (jsonNode.isArray()) {
for (JsonNode node : jsonNode) {
responseBody.add(node.toString());
}
} else {
responseBody.add(jsonNode.toString());
}
}
connection.disconnect();} catch (IOException e) {
logger.error(e.getMessage(), e);
}return responseBody.stream().map(god -> god.replace("\"", "")).toList();
}
```- Javadoc: https://docs.oracle.com/javase/8/docs/api/java/net/URLConnection.html
But this approach involve several low-level parts for a simple http interaction.
In order to help the developers, Spring ecosystem has delivered features to offer HTTP support for Servlet environments from the beginning using the client *RESTTemplate* which implements the Template Design pattern.
- Javadoc Spring Framework 5, Spring Boot 2.x: https://docs.spring.io/spring-framework/docs/5.3.9/javadoc-api/org/springframework/web/client/RestTemplate.html
- Javadoc Spring Framework 6, Spring Boot 3.x: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html```java
public List getGods() {
ResponseEntity> result =
restTemplate.exchange(
address,
HttpMethod.GET,
null,
new ParameterizedTypeReference<>() {}
);return result.getBody();
}
```In Java 11, the language published a new way to interact with http protocol
```java
public List getGods() {List responseBody = new ArrayList<>();
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(address))
.header("Content-Type", "application/json")
.GET()
.build();HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(response.body());
if (jsonNode.isArray()) {
for (JsonNode node : jsonNode) {
responseBody.add(node.toString());
}
} else {
responseBody.add(jsonNode.toString());
}
}} catch (IOException | InterruptedException e) {
logger.error(e.getMessage(), e);
}return responseBody.stream().map(god -> god.replace("\"", "")).toList();
}
```- Javadocs: https://docs.oracle.com/en%2Fjava%2Fjavase%2F11%2Fdocs%2Fapi%2F%2F/java.net.http/java/net/http/HttpClient.html
In the last Decade, Java ecosystem evolved with the reactive programming paradym and Spring ecosystem adapted to it and in 2013, Spring released the first GA release for Reactor:
- https://mvnrepository.com/artifact/org.projectreactor/reactor-spring/1.0.0.RELEASE
and in 2017, Spring Boot released the reactive support for it:
- https://mvnrepository.com/artifact/org.springframework/spring-webflux
and webflux provided a Reactive client:
```java
public Mono> getGods() {
return webClient
.get()
.retrieve()
.bodyToMono(new ParameterizedTypeReference>() {});
}
```in 2022 with the release of Spring Framework 6.1, it included **HTTP Interfaces**
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/service/annotation/GetExchange.html
```java
import org.springframework.web.service.annotation.GetExchange;import java.util.List;
public interface GodService {
@GetExchange()
List getGods();
}
```in 2024, Spring Boot 3.2 release, it included a new HTTP Client based on fluent API design named **RestClient**.
- Javadoc Spring Framework 6, Spring Boot 3.2 >: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestClient.html
```java
public List getGods() {
ResponseEntity> result =
this.restClient
.get()
.uri(address)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.toEntity(new ParameterizedTypeReference<>() {});
return result.getBody();
}
```Note: Obviously, to that Spring Solutions, you can configure:
```java
@Configuration(proxyBeanMethods = false)
public class WebConfiguration {//Spring RestTemplate
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}//Spring RestClient
@Bean
RestClient restClient(RestTemplate restTemplate) {
return RestClient.create(restTemplate);
}/*
@Bean
RestClient restClient(RestClient.Builder builder) {
return builder.build();
}
*///Http Interfaces
@Bean(name = "http-interface-rest-client")
GodService godServiceRestClient(RestClient client) {
return HttpServiceProxyFactory
.builderFor(RestClientAdapter.create(client))
.build()
.createClient(GodService.class);
}
}
```## What is the level of activity about RestClient?
- https://github.com/spring-projects/spring-boot/issues?q=is%3Aissue+restclient+is%3Aclosed
## How to use the client:
From the documentation, you could use the new client in several ways:
- https://docs.spring.io/spring-framework/reference/integration/rest-clients.html
```java
//General usage
RestClient defaultClient = RestClient.create();RestClient customClient = RestClient.builder()
.requestFactory(new HttpComponentsClientHttpRequestFactory())
.messageConverters(converters -> converters.add(new MyCustomMessageConverter()))
.baseUrl("https://example.com")
.defaultUriVariables(Map.of("variable", "foo"))
.defaultHeader("My-Header", "Foo")
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
.build();//GET Retrieving a Primitive type
String result = restClient.get()
.uri("https://example.com")
.retrieve()
.body(String.class);//GET Retrieving an Single Object
int id = ...;
Pet pet = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id)
.accept(APPLICATION_JSON)
.retrieve()
.body(Pet.class);//GET Retrieve a List of Objects
List articles = restClient.get()
.uri(uriBase + "/articles")
.retrieve()
.body(new ParameterizedTypeReference<>() {});//POST Sending a new Object as payload
Pet pet = new Pet();
ResponseEntity response = restClient.post()
.uri("https://petclinic.example.com/pets/new")
.contentType(APPLICATION_JSON)
.body(pet)
.retrieve()
.toBodilessEntity();//Error handling
String result = restClient.get()
.uri("https://example.com/this-url-does-not-exist")
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> {
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders())
})
.body(String.class);//Complex stuff
Pet result = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id)
.accept(APPLICATION_JSON)
.exchange((request, response) -> {
if (response.getStatusCode().is4xxClientError()) {
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders());
}
else {
Pet pet = convertResponse(response);
return pet;
}
});
```## Another alternatives
- https://square.github.io/okhttp/
- https://hc.apache.org/httpcomponents-client-5.3.x/index.html## Further details
- https://github.com/jabrena/spring-boot-user-beans
## References
- https://en.wikipedia.org/wiki/HTTP
- https://start.spring.io/
- https://docs.spring.io/spring-framework/reference/integration/rest-clients.html
- https://docs.spring.io/spring-framework/docs/
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestClient.html
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/service/annotation/GetExchange.html
- https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/client/RestClient.java
- https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java
- https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/client/DefaultRestClient.java
- https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/test/java/org/springframework/web/client/RestClientIntegrationTests.java
- https://github.com/spring-projects/spring-framework/issues/29552
- https://spring.io/guides/gs/consuming-rest/