HttpClient déclaratif de Spring 7 : l'alternative à FeignClient
Spring 7 introduit un HttpClient déclaratif natif basé sur RestClient et WebClient. Il utilise des annotations familières comme @GetExchange et @PostExchange, similaires à @GetMapping dans les contrôleurs. Cela permet de définir des requêtes HTTP sortantes via des interfaces, sans implémentation manuelle.
Voici à quoi ressemble l'interface principale :
public interface UserClient {
@GetExchange("/api/users/{id}")
UserResponse getUser(
@PathVariable("id") Long id,
@RequestParam(name = "includeDetails", defaultValue = "false") boolean includeDetails,
@RequestHeader("Authorization") String authToken,
@RequestHeader("X-Request-Id") String requestId
);
@PostExchange("/api/users")
UserResponse createUser(
@RequestBody @Valid CreateUserRequest request,
@RequestHeader("Authorization") String authToken
);
@PatchExchange("/api/users/{id}")
void updateUserEmail(
@PathVariable("id") Long id,
@RequestParam("email") String email,
@RequestHeader("Authorization") String authToken
);
}
HttpClient s'intègre nativement à l'observabilité et ne nécessite pas Spring Cloud — contrairement à FeignClient, en mode maintenance depuis 2022.
Intégration avec OpenAPI Generator
Pour générer automatiquement les clients, utilisez openapi-generator avec la bibliothèque spring-http-interface.
Configuration du plugin Maven :
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>${openapi-generator.version}</version>
<executions>
<execution>
<id>generate-weather-client</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/openapi/external/weather-api.yaml</inputSpec>
<generatorName>spring</generatorName>
<library>spring-http-interface</library>
<output>${project.build.directory}/generated-sources/openapi-client</output>
<apiPackage>ulllie.exchange.openapi.gen.client.api</apiPackage>
<modelPackage>ulllie.exchange.openapi.gen.client.model</modelPackage>
<generateApis>true</generateApis>
<generateModels>true</generateModels>
<generateSupportingFiles>false</generateSupportingFiles>
<configOptions>
<useSpringBoot4>true</useSpringBoot4>
<useJackson3>true</useJackson3>
<interfaceOnly>true</interfaceOnly>
<skipDefaultInterface>true</skipDefaultInterface>
<useBeanValidation>true</useBeanValidation>
<annotationLibrary>none</annotationLibrary>
<serializationLibrary>jackson</serializationLibrary>
<useTags>true</useTags>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
Cela génère une interface comme :
public interface OpenMeteoApi {
@HttpExchange(method = "GET", value = "/v1/forecast")
ResponseEntity<ForecastResponse> getForecast(
@RequestParam("latitude") Double latitude,
@RequestParam("longitude") Double longitude
);
}
Connexion et configuration des services
Configuration via import :
@Configuration @ImportHttpServices(group = "weather", types = OpenMeteoApi.class)
public class WeatherRestClientConfig implements RestClientHttpServiceGroupConfigurer {
@Override
public void configureGroups(Groups<RestClient.Builder> groups) {
groups.filterByName("weather").forEachClient(
($, builder) -> builder.baseUrl("https://api.weather.com")
);
}
}
La gestion d'erreurs se configure via defaultStatusHandler :
builder.baseUrl(properties.baseUrl())
.defaultStatusHandler(
status -> status.is4xxClientError() || status.is5xxServerError(),
errorHandler
);
Un HttpErrorHandler personnalisé permet d'adapter la logique pour les codes 4xx/5xx, d'intégrer Resilience4j ou d'utiliser @ControllerAdvice.
Fonctionnalités clés :
- Configuration d'intercepteurs
- Intégration Resilience4j
- Gestion d'erreurs au niveau ResponseSpec
RestClient vs WebClient avec Project Loom
Avec les threads virtuels (spring.threads.virtual.enabled=true), le RestClient bloquant offre une scalabilité I/O sans programmation réactive.
Avantages :
- Code impératif facile à déboguer
- Pas de pipelines réactifs
- Performances équivalentes à WebClient pour les tâches I/O
Pour les scénarios réactifs, optez pour WebClient.Builder afin de générer des signatures Mono/Flux.
Défis de validation et solutions
Bean Validation ne parcourt pas récursivement les objets imbriqués car le générateur omet @Valid.
Solutions :
- x-field-extra-annotation dans OpenAPI :
@jakarta.validation.Valid— nécessite des modifications de spécification. - AlwaysTraversableResolver — parcourt l'arbre d'objets en entier globalement, mais risque des cycles et pertes de performance.
- Modèles Mustache personnalisés — ajoute automatiquement
@Validlors de la génération.
Bonus : Générez des data classes Kotlin pour la sécurité null.
data class ForecastResponse(
val temperature: Double,
val windSpeed: Double?
)
Dépendances : kotlin-stdlib + jackson-module-kotlin. Détection rapide des erreurs de nullabilité lors de la désérialisation.
Points clés à retenir
- HttpClient est natif au Spring Framework — sans dépendances Spring Cloud.
- OpenAPI generator avec
spring-http-interfaceproduit des interfaces prêtes à l'emploi. - Les threads virtuels simplifient l'I/O sans réactivité.
- Les data classes Kotlin détectent tôt les problèmes de null.
- Gestion d'erreurs et validation entièrement personnalisables.
— Editorial Team
Aucun commentaire pour le moment.