Introduction à RestTemplate
Qu'est-ce que RestTemplate ?
Spring RestTemplate est un client HTTP synchrone qui simplifie la communication avec des services REST. C'est l'outil idéal pour qu'un microservice communique avec un autre service.
Client RestTemplate] --> B[RestTemplate] B --> C[Service B
Users API] B --> D[Service C
Products API] style A fill:#FF9800,stroke:#E65100 style B fill:#4CAF50,stroke:#388E3C style C fill:#2196F3,stroke:#0D47A1 style D fill:#9C27B0,stroke:#4A148C
Avantages de RestTemplate :
- Simplicité : Méthodes simples pour toutes les opérations HTTP
- Intégration Spring : Fonctionne parfaitement avec l'écosystème Spring
- Sérialisation automatique : Conversion JSON ↔ Objets Java automatique
- Gestion d'erreurs : Gestion intégrée des exceptions HTTP
Architecture des Microservices
Structure de l'application
Port: 8761] --> B[Microservice A
Client - Port: 8080] A --> C[Microservice B
Users - Port: 8081] A --> D[Microservice C
Products - Port: 8082] B -- RestTemplate --> C B -- RestTemplate --> D style A fill:#2196F3,stroke:#0D47A1 style B fill:#FF9800,stroke:#E65100 style C fill:#4CAF50,stroke:#388E3C style D fill:#9C27B0,stroke:#4A148C
Rôles des microservices :
- Microservice A : Client qui utilise RestTemplate pour communiquer
- Microservice B : Service de gestion des utilisateurs
- Microservice C : Service de gestion des produits
- Eureka Server : Serveur de découverte de services
Microservice B - Service Utilisateurs (Simple)
1 Structure du projet
Arborescence du projet :
microserviceB/
├── pom.xml
└── src/main/
├── java/com/example/users/
│ ├── UsersMicroserviceApplication.java
│ ├── controller/
│ │ └── UserController.java
│ ├── model/
│ │ └── User.java
└── resources/
└── application.properties
2 Fichier pom.xml
Dépendances Maven :
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka Client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
3 Configuration application.properties
Fichier de configuration :
# === CONFIGURATION SERVEUR ===
server.port=8081
spring.application.name=service-b
# === CONFIGURATION EUREKA ===
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.instance.prefer-ip-address=true
4 Modèle User
Modèle User :
package com.example.users.model;
/**
* Modèle représentant un utilisateur
*/
public class User {
private Long id;
private String name;
private String email;
private String department;
// Constructeurs
// Getters et Setters
5 Contrôleur REST
Contrôleur REST :
package com.example.users.controller;
import com.example.users.model.User;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* Contrôleur REST pour les opérations sur les utilisateurs
*/
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "*")
public class UserController {
// Stockage en mémoire pour la démonstration
private final ConcurrentHashMap<Long, User> users = new ConcurrentHashMap<>();
private final AtomicLong counter = new AtomicLong();
// Initialisation avec quelques utilisateurs
public UserController() {
users.put(counter.incrementAndGet(), new User(1L, "John Doe", "john@example.com", "IT"));
users.put(counter.incrementAndGet(), new User(2L, "Jane Smith", "jane@example.com", "HR"));
users.put(counter.incrementAndGet(), new User(3L, "Bob Johnson", "bob@example.com", "Finance"));
counter.set(3);
}
/**
* Récupère tous les utilisateurs
* @return Liste d'utilisateurs
*/
@GetMapping
public List<User> getAllUsers() {
return new ArrayList<>(users.values());
}
/**
* Récupère un utilisateur par son ID
* @param id ID de l'utilisateur
* @return Utilisateur trouvé
*/
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return users.get(id);
}
/**
* Crée un nouvel utilisateur
* @param user Utilisateur à créer
* @return Utilisateur créé
*/
@PostMapping
public User createUser(@RequestBody User user) {
Long id = counter.incrementAndGet();
user.setId(id);
users.put(id, user);
return user;
}
/**
* Met à jour un utilisateur existant
* @param id ID de l'utilisateur
* @param user Données de mise à jour
* @return Utilisateur mis à jour
*/
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
user.setId(id);
users.put(id, user);
return user;
}
/**
* Supprime un utilisateur
* @param id ID de l'utilisateur à supprimer
*/
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
users.remove(id);
}
/**
* Recherche par département
*/
@GetMapping(params = "department")
public List<User> getUsersByDepartment(@RequestParam String department) {
List<User> result = new ArrayList<>();
for (User user : users.values()) {
if (user.getDepartment().equals(department)) {
result.add(user);
}
}
return result;
}
}
6 Classe principale
Application Spring Boot :
package com.example.users;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* Classe principale du microservice de gestion des utilisateurs
*/
@SpringBootApplication
@EnableDiscoveryClient
public class UsersMicroserviceApplication {
public static void main(String[] args) {
SpringApplication.run(UsersMicroserviceApplication.class, args);
}
}
Microservice C - Service Produits (Simple)
1 Structure du projet
Arborescence du projet :
microserviceC/
├── pom.xml
└── src/main/
├── java/com/example/products/
│ ├── ProductsMicroserviceApplication.java
│ ├── controller/
│ │ └── ProductController.java
│ ├── model/
│ │ └── Product.java
└── resources/
└── application.properties
2 Fichier pom.xml
Dépendances Maven :
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka Client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
3 Configuration application.properties
Fichier de configuration :
# === CONFIGURATION SERVEUR ===
server.port=8082
spring.application.name=service-c
# === CONFIGURATION EUREKA ===
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.instance.prefer-ip-address=true
4 Modèle Product
Modèle Product :
package com.example.products.model;
/**
* Modèle représentant un produit
*/
public class Product {
private Long id;
private String name;
private String description;
private Double price;
private String category;
// Constructeurs
// Getters et Setters
5 Contrôleur REST
Contrôleur REST :
package com.example.products.controller;
import com.example.products.model.Product;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* Contrôleur REST pour les opérations sur les produits
*/
@RestController
@RequestMapping("/api/products")
@CrossOrigin(origins = "*")
public class ProductController {
// Stockage en mémoire pour la démonstration
private final ConcurrentHashMap<Long, Product> products = new ConcurrentHashMap<>();
private final AtomicLong counter = new AtomicLong();
// Initialisation avec quelques produits
public ProductController() {
products.put(counter.incrementAndGet(), new Product(1L, "Laptop", "High-performance laptop", 999.99, "Electronics"));
products.put(counter.incrementAndGet(), new Product(2L, "Smartphone", "Latest smartphone model", 699.99, "Electronics"));
products.put(counter.incrementAndGet(), new Product(3L, "Coffee Mug", "Ceramic coffee mug", 12.99, "Kitchen"));
counter.set(3);
}
/**
* Récupère tous les produits
* @return Liste de produits
*/
@GetMapping
public List<Product> getAllProducts() {
return new ArrayList<>(products.values());
}
/**
* Récupère un produit par son ID
* @param id ID du produit
* @return Produit trouvé
*/
@GetMapping("/{id}")
public Product getProductById(@PathVariable Long id) {
return products.get(id);
}
/**
* Crée un nouveau produit
* @param product Produit à créer
* @return Produit créé
*/
@PostMapping
public Product createProduct(@RequestBody Product product) {
Long id = counter.incrementAndGet();
product.setId(id);
products.put(id, product);
return product;
}
/**
* Met à jour un produit existant
* @param id ID du produit
* @param product Données de mise à jour
* @return Produit mis à jour
*/
@PutMapping("/{id}")
public Product updateProduct(@PathVariable Long id, @RequestBody Product product) {
product.setId(id);
products.put(id, product);
return product;
}
/**
* Supprime un produit
* @param id ID du produit à supprimer
*/
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable Long id) {
products.remove(id);
}
/**
* Recherche par catégorie
*/
@GetMapping(params = "category")
public List<Product> getProductsByCategory(@RequestParam String category) {
List<Product> result = new ArrayList<>();
for (Product product : products.values()) {
if (product.getCategory().equals(category)) {
result.add(product);
}
}
return result;
}
/**
* Recherche par prix maximum
*/
@GetMapping(params = "maxPrice")
public List<Product> getProductsByMaxPrice(@RequestParam Double maxPrice) {
List<Product> result = new ArrayList<>();
for (Product product : products.values()) {
if (product.getPrice() <= maxPrice) {
result.add(product);
}
}
return result;
}
}
6 Classe principale
Application Spring Boot :
package com.example.products;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* Classe principale du microservice de gestion des produits
*/
@SpringBootApplication
@EnableDiscoveryClient
public class ProductsMicroserviceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductsMicroserviceApplication.class, args);
}
}
Microservice A - Client RestTemplate (Fonctionnalités Avancées)
1 Configuration de Base de RestTemplate
Configuration Spring Boot :
package com.example.client.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import java.nio.charset.StandardCharsets;
/**
* Configuration de base de RestTemplate
*/
@Configuration
public class RestTemplateConfig {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
// Configuration des convertisseurs de messages
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
}
Explication détaillée :
RestTemplate est le client HTTP principal de Spring pour effectuer des appels REST. Il gère automatiquement la sérialisation/désérialisation JSON, les conversions de types, et fournit des méthodes simples pour toutes les opérations HTTP.
GetMapping : Utilisé pour les requêtes HTTP GET
PostMapping : Utilisé pour les requêtes HTTP POST
PutMapping : Utilisé pour les requêtes HTTP PUT
DeleteMapping : Utilisé pour les requêtes HTTP DELETE
2 Modèles de Données
package com.example.client.model;
// Même structure que dans le microservice B
public class User {
private Long id;
private String name;
private String email;
private String department;
// Constructeurs, getters
package com.example.client.model;
// Même structure que dans le microservice C
public class Product {
private Long id;
private String name;
private String description;
private Double price;
private String category;
// Constructeurs, getters et setters
3 Service RestTemplate - Fonctionnalités de Base
Service de communication avec méthodes de base :
package com.example.client.service;
import com.example.client.model.User;
import com.example.client.model.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.Arrays;
import java.util.List;
/**
* Service démontrant les fonctionnalités de base de RestTemplate
*/
@Service
public class BasicRestTemplateService {
@Autowired
private RestTemplate restTemplate;
// === MÉTHODES GET - Récupération de données ===
/**
* getForObject() - Méthode la plus simple pour récupérer un objet
* Utilisée quand on ne besoin que du corps de la réponse
*/
public User getUserById(Long id) {
String url = "http://service-b/api/users/" + id;
// getForObject(url, typeDeRetour)
return restTemplate.getForObject(url, User.class);
}
/**
* getForEntity() - Récupère l'objet ET les métadonnées (headers, status code)
* Utilisée quand on a besoin d'informations supplémentaires
*/
public ResponseEntity<User> getUserWithDetails(Long id) {
String url = "http://service-b/api/users/" + id;
// getForEntity(url, typeDeRetour) - retourne ResponseEntity
return restTemplate.getForEntity(url, User.class);
}
/**
* Récupération d'une liste d'objets
* getForObject ne gère pas directement List, donc on utilise un tableau
*/
public List<User> getAllUsers() {
String url = "http://service-b/api/users";
// On récupère un tableau et on le convertit en liste
User[] usersArray = restTemplate.getForObject(url, User[].class);
return Arrays.asList(usersArray);
}
/**
* Récupération avec paramètres de requête
* Utilisation de UriComponentsBuilder pour construire l'URL
*/
public List<User> getUsersByDepartment(String department) {
// Construction sécurisée de l'URL avec paramètres
String url = UriComponentsBuilder.fromHttpUrl("http://service-b/api/users")
.queryParam("department", department)
.toUriString();
User[] usersArray = restTemplate.getForObject(url, User[].class);
return Arrays.asList(usersArray);
}
// === MÉTHODES POST - Création de ressources ===
/**
* postForObject() - Crée une ressource et retourne l'objet créé
* La sérialisation JSON est automatique
*/
public User createUser(User user) {
String url = "http://service-b/api/users";
// postForObject(url, objetAEnvoyer, typeDeRetour)
return restTemplate.postForObject(url, user, User.class);
}
/**
* postForEntity() - Crée une ressource et retourne ResponseEntity
* Utile pour accéder aux headers et au code status
*/
public ResponseEntity<User> createUserWithResponse(User user) {
String url = "http://service-b/api/users";
// postForEntity(url, objetAEnvoyer, typeDeRetour)
return restTemplate.postForEntity(url, user, User.class);
}
/**
* postForLocation() - Crée une ressource et retourne l'URI de la ressource créée
* Utile pour obtenir l'URL de la nouvelle ressource
*/
public URI createUserAndGetLocation(User user) {
String url = "http://service-b/api/users";
// postForLocation(url, objetAEnvoyer) - retourne URI
return restTemplate.postForLocation(url, user);
}
// === MÉTHODES PUT - Mise à jour de ressources ===
/**
* put() - Met à jour une ressource
* Ne retourne rien (void)
*/
public void updateUser(Long id, User user) {
String url = "http://service-b/api/users/" + id;
// put(url, objetMisAJour)
restTemplate.put(url, user);
}
// === MÉTHODES DELETE - Suppression de ressources ===
/**
* delete() - Supprime une ressource
* Ne retourne rien (void)
*/
public void deleteUser(Long id) {
String url = "http://service-b/api/users/" + id;
// delete(url)
restTemplate.delete(url);
}
/**
* delete() avec URI - Suppression avec construction d'URI
*/
public void deleteUserWithUri(Long id) {
// Construction d'URI avec variables
String url = "http://service-b/api/users/{id}";
// delete(url, variables...)
restTemplate.delete(url, id);
}
}
Explication des méthodes de base :
getForObject(url, responseType) : La méthode la plus simple pour récupérer un objet. Retourne directement l'objet désérialisé.
getForEntity(url, responseType) : Récupère un objet avec ses métadonnées (headers, status code). Retourne un ResponseEntity.
postForObject(url, request, responseType) : Crée une ressource et retourne l'objet créé. L'objet est automatiquement sérialisé en JSON.
postForEntity(url, request, responseType) : Crée une ressource et retourne ResponseEntity avec métadonnées.
postForLocation(url, request) : Crée une ressource et retourne l'URI de la nouvelle ressource.
put(url, request) : Met à jour une ressource. Ne retourne rien.
delete(url) : Supprime une ressource. Ne retourne rien.
6 Contrôleur REST Client
Contrôleur exposant les fonctionnalités :
package com.example.client.controller;
import com.example.client.model.User;
import com.example.client.model.Product;
import com.example.client.service.BasicRestTemplateService;
//import com.example.client.service.AdvancedRestTemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* Contrôleur REST pour exposer les fonctionnalités du client RestTemplate
*/
@RestController
@RequestMapping("/api/client")
@CrossOrigin(origins = "*")
public class ClientController {
@Autowired
private BasicRestTemplateService basicService;
// === ENDPOINTS FONCTIONNALITÉS DE BASE ===
@GetMapping("/users/{id}")
public User getUserById(@PathVariable Long id) {
return basicService.getUserById(id);
}
@GetMapping("/users/{id}/details")
public ResponseEntity<User> getUserWithDetails(@PathVariable Long id) {
return basicService.getUserWithDetails(id);
}
@GetMapping("/users")
public List<User> getAllUsers() {
return basicService.getAllUsers();
}
@GetMapping("/users/department/{department}")
public List<User> getUsersByDepartment(@PathVariable String department) {
return basicService.getUsersByDepartment(department);
}
@PostMapping("/users")
public User createUser(@RequestBody User user) {
return basicService.createUser(user);
}
@PostMapping("/users/with-response")
public ResponseEntity<User> createUserWithResponse(@RequestBody User user) {
return basicService.createUserWithResponse(user);
}
@PutMapping("/users/{id}")
public void updateUser(@PathVariable Long id, @RequestBody User user) {
basicService.updateUser(id, user);
}
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable Long id) {
basicService.deleteUser(id);
}
}
Tester les fonctionalities de base pusis passer aux fonctionalities AVANCÉES
4 Service RestTemplate - Fonctionnalités Avancées
Service avec méthodes avancées :
package com.example.client.service;
import com.example.client.model.User;
import com.example.client.model.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.List;
import java.util.Arrays;
/**
* Service démontrant les fonctionnalités avancées de RestTemplate
*/
@Service
public class AdvancedRestTemplateService {
@Autowired
private RestTemplate restTemplate;
// === UTILISATION DE HttpEntity ET HttpHeaders ===
/**
* Envoi personnalisé avec headers HTTP
* HttpEntity permet d'envoyer des headers personnalisés avec le corps
*/
public ResponseEntity<User> createUserWithCustomHeaders(User user) {
String url = "http://service-b/api/users";
// Création des headers personnalisés
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.set("X-API-Key", "votre-cle-api");
headers.set("User-Agent", "MonApplication/1.0");
// Création de l'entité HTTP (headers + corps)
HttpEntity<User> requestEntity = new HttpEntity<>(user, headers);
// Envoi avec exchange() - méthode la plus flexible
return restTemplate.exchange(
url, // URL
HttpMethod.POST, // Méthode HTTP
requestEntity, // Entité HTTP (headers + corps)
User.class // Type de retour
);
}
/**
* Récupération avec headers personnalisés
* Utile pour l'authentification, le cache, etc.
*/
public ResponseEntity<List<User>> getAllUsersWithHeaders() {
String url = "http://service-b/api/users";
// Création des headers
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.set("Authorization", "Bearer votre-token-jwt");
headers.set("If-None-Match", "version-cache");
// Entité HTTP avec headers seulement
HttpEntity<String> entity = new HttpEntity<>(headers);
// Utilisation de ParameterizedTypeReference pour List
return restTemplate.exchange(
url,
HttpMethod.GET,
entity,
new ParameterizedTypeReference<List<User>>() {}
);
}
// === GESTION DES TYPES COMPLEXES AVEC ParameterizedTypeReference ===
/**
* Récupération de listes complexes avec ParameterizedTypeReference
* Nécessaire pour les types génériques comme List
*/
public List<User> getUsersByDepartmentAdvanced(String department) {
String url = UriComponentsBuilder.fromHttpUrl("http://service-b/api/users")
.queryParam("department", department)
.toUriString();
// Headers personnalisés
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<>(headers);
// exchange() avec ParameterizedTypeReference pour List
ResponseEntity<List<User>> response = restTemplate.exchange(
url,
HttpMethod.GET,
entity,
new ParameterizedTypeReference<List<User>>() {}
);
return response.getBody();
}
// === GESTION DES ERREURS HTTP ===
/**
* Gestion robuste des erreurs HTTP
* Capture des exceptions spécifiques aux erreurs client/serveur
*/
public User getUserByIdWithErrorHandling(Long id) {
String url = "http://service-b/api/users/" + id;
try {
return restTemplate.getForObject(url, User.class);
} catch (HttpClientErrorException e) {
// Erreurs 4xx (404 Not Found, 400 Bad Request, etc.)
System.err.println("Erreur client HTTP: " + e.getStatusCode());
System.err.println("Réponse: " + e.getResponseBodyAsString());
throw new RuntimeException("Utilisateur non trouvé ou requête invalide", e);
} catch (HttpServerErrorException e) {
// Erreurs 5xx (500 Internal Server Error, 503 Service Unavailable, etc.)
System.err.println("Erreur serveur HTTP: " + e.getStatusCode());
System.err.println("Réponse: " + e.getResponseBodyAsString());
throw new RuntimeException("Erreur du service utilisateur", e);
} catch (Exception e) {
// Autres erreurs (réseau, timeout, etc.)
System.err.println("Erreur inattendue: " + e.getMessage());
throw new RuntimeException("Impossible de contacter le service utilisateur", e);
}
}
/**
* Vérification du code status avant traitement
* Approche proactive pour gérer les erreurs
*/
public User getUserWithStatusCheck(Long id) {
String url = "http://service-b/api/users/" + id;
// Récupération avec ResponseEntity pour accéder au status
ResponseEntity<User> response = restTemplate.getForEntity(url, User.class);
// Vérification du code status
if (response.getStatusCode().is2xxSuccessful()) {
return response.getBody();
} else if (response.getStatusCode().is4xxClientError()) {
throw new RuntimeException("Erreur client: " + response.getStatusCode());
} else if (response.getStatusCode().is5xxServerError()) {
throw new RuntimeException("Erreur serveur: " + response.getStatusCode());
} else {
throw new RuntimeException("Code status inattendu: " + response.getStatusCode());
}
}
// === UTILISATION DE exchange() - LA MÉTHODE ULTIME ===
/**
* exchange() - Méthode la plus puissante et flexible
* Permet de tout contrôler : méthode HTTP, headers, corps, type de retour
*/
public ResponseEntity<User> performCustomRequest(String method, String url, Object requestBody, Class<User> responseType) {
HttpMethod httpMethod = HttpMethod.valueOf(method.toUpperCase());
// Création des headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
// Création de l'entité HTTP
HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody, headers);
// Exécution de la requête personnalisée
return restTemplate.exchange(
url,
httpMethod,
requestEntity,
responseType
);
}
/**
* exchange() avec requête PATCH (non supportée nativement par RestTemplate)
*/
public ResponseEntity<User> updateUserPartially(Long id, User partialUser) {
String url = "http://service-b/api/users/" + id;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<User> requestEntity = new HttpEntity<>(partialUser, headers);
return restTemplate.exchange(
url,
HttpMethod.PATCH, // PATCH supporté via exchange()
requestEntity,
User.class
);
}
// === REQUÊTES AVEC VARIABLES D'URI ===
/**
* Utilisation de variables d'URI pour des URLs dynamiques
* Plus sécurisé que la concaténation de chaînes
*/
public User getUserByIdWithUriVariables(Long id) {
String url = "http://service-b/api/users/{userId}";
// Passage des variables par ordre
return restTemplate.getForObject(url, User.class, id);
// Ou passage des variables par nom
// return restTemplate.getForObject(url, User.class, Collections.singletonMap("userId", id));
}
/**
* Requête complexe avec plusieurs variables
*/
public List<Product> searchProducts(String category, Double maxPrice) {
String url = "http://service-c/api/products?category={category}&maxPrice={maxPrice}";
// Passage des variables par ordre
Product[] products = restTemplate.getForObject(
url,
Product[].class,
category,
maxPrice
);
return Arrays.asList(products);
}
}
Explication des fonctionnalités avancées :
HttpEntity<T> : Classe qui encapsule à la fois le corps de la requête et les headers HTTP. Permet un contrôle total sur les requêtes sortantes.
HttpHeaders : Classe pour manipuler les headers HTTP. Permet d'ajouter des headers personnalisés comme Authorization, Content-Type, Accept, etc.
exchange() : Méthode la plus flexible de RestTemplate. Permet de spécifier la méthode HTTP, les headers, le corps de la requête, et le type de retour. Supporte toutes les méthodes HTTP y compris PATCH.
ParameterizedTypeReference : Nécessaire pour désérialiser des types génériques comme List<User>. Contourne les limitations de l'effacement de type en Java.
Gestion des erreurs : Capture des exceptions spécifiques HttpClientErrorException (4xx) et HttpServerErrorException (5xx) pour une gestion précise des erreurs HTTP.
Variables d'URI : Utilisation de {variable} dans les URLs pour une construction sécurisée et éviter les injections.
5 Configuration Avancée de RestTemplate
Configuration avec timeout, pooling, etc. :
package com.example.client.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* Configuration avancée de RestTemplate avec timeout et pooling
*/
@Configuration
public class AdvancedRestTemplateConfig {
@Bean
public RestTemplate advancedRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
// Configuration avancée avec Apache HttpClient
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory();
// Timeout de connexion (temps pour établir la connexion)
factory.setConnectTimeout(5000); // 5 secondes
// Timeout de lecture (temps pour lire la réponse)
factory.setReadTimeout(10000); // 10 secondes
// Timeout de demande (temps pour obtenir une connexion du pool)
factory.setConnectionRequestTimeout(2000); // 2 secondes
restTemplate.setRequestFactory(factory);
return restTemplate;
}
}
package com.example.client.interceptor;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* Intercepteur pour logger les requêtes et réponses HTTP
*/
public class LoggingInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(
HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution) throws IOException {
// Log de la requête
logRequest(request, body);
// Exécution de la requête
ClientHttpResponse response = execution.execute(request, body);
// Log de la réponse
logResponse(response);
return response;
}
private void logRequest(HttpRequest request, byte[] body) {
System.out.println("=== HTTP REQUEST ===");
System.out.println("URI: " + request.getURI());
System.out.println("Method: " + request.getMethod());
System.out.println("Headers: " + request.getHeaders());
System.out.println("Body: " + new String(body, StandardCharsets.UTF_8));
}
private void logResponse(ClientHttpResponse response) throws IOException {
System.out.println("=== HTTP RESPONSE ===");
System.out.println("Status: " + response.getStatusCode());
System.out.println("Headers: " + response.getHeaders());
System.out.println("Body: " + StreamUtils.copyToString(
response.getBody(), StandardCharsets.UTF_8));
}
}
package com.example.client.config;
import com.example.client.interceptor.LoggingInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import java.util.Collections;
/**
* Configuration de RestTemplate avec intercepteur
*/
@Configuration
public class InterceptedRestTemplateConfig {
@LoadBalanced
@Bean
public RestTemplate interceptedRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
// Ajout de l'intercepteur pour le logging
restTemplate.setInterceptors(Collections.singletonList(new LoggingInterceptor()));
return restTemplate;
}
}
6 Contrôleur REST Client
Contrôleur exposant les fonctionnalités :
package com.example.client.controller;
import com.example.client.model.User;
import com.example.client.model.Product;
import com.example.client.service.AdvancedRestTemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* Contrôleur REST pour exposer les fonctionnalités du client RestTemplate
*/
@RestController
@RequestMapping("/api/client2")
@CrossOrigin(origins = "*")
public class ClientController {
@Autowired
private AdvancedRestTemplateService advancedService;
// === ENDPOINTS FONCTIONNALITÉS DE BASE ===
@GetMapping("/users/with-headers")
public ResponseEntity<List<User>> getAllUsersWithHeaders() {
return advancedService.getAllUsersWithHeaders();
}
@GetMapping("/users/department/{department}/advanced")
public List<User> getUsersByDepartmentAdvanced(@PathVariable String department) {
return advancedService.getUsersByDepartmentAdvanced(department);
}
@GetMapping("/users/{id}/safe")
public User getUserByIdWithErrorHandling(@PathVariable Long id) {
return advancedService.getUserByIdWithErrorHandling(id);
}
@GetMapping("/users/{id}/checked")
public User getUserWithStatusCheck(@PathVariable Long id) {
return advancedService.getUserWithStatusCheck(id);
}
@PatchMapping("/users/{id}")
public ResponseEntity<User> updateUserPartially(@PathVariable Long id, @RequestBody User partialUser) {
return advancedService.updateUserPartially(id, partialUser);
}
@GetMapping("/users/{id}/uri-variables")
public User getUserByIdWithUriVariables(@PathVariable Long id) {
return advancedService.getUserByIdWithUriVariables(id);
}
}
Tableau Récapitulatif des Fonctionnalités
| Fonctionnalité | Méthode RestTemplate | Utilisation | Avantages |
|---|---|---|---|
| Récupération simple | getForObject() | Quand seul le corps de la réponse est nécessaire | Simplicité, conversion automatique |
| Récupération avec métadonnées | getForEntity() | Quand headers/status code sont nécessaires | Accès complet à la réponse HTTP |
| Création de ressource | postForObject() | Création et retour de l'objet créé | Simplicité, sérialisation automatique |
| Création avec réponse détaillée | postForEntity() | Création avec accès aux headers | Contrôle total sur la réponse |
| Obtention de l'URI créée | postForLocation() | Quand seule l'URI est nécessaire | Performance optimale |
| Mise à jour | put() | Mise à jour de ressources existantes | Simplicité, pas de retour nécessaire |
| Suppression | delete() | Suppression de ressources | Simplicité, pas de retour nécessaire |
| Requêtes personnalisées | exchange() | Contrôle total sur la requête | Flexibilité maximale, support de toutes les méthodes |