Lors du développement d'une application Spring Cloud, une collègue a rencontré une erreur lors de l'utilisation de @FeignClient pour uploader des fichiers. Le paramètre en entrée était de type MultipartFile, ce qui a révélé une incompatibilité connue : Feign ne gère pas nativement les requêtes multipart/form-data. La résolution nécessite l'intégration de modules complémentaires, mais leur version doit correspondre à celle d'OpenFeign utilisée.
La documentation du projet feign-form précise les dépendances versionnées :
- Les versions de
feign-formantérieures à 3.5.0 fonctionnent avec OpenFeign 9.*. - À partir de
feign-form3.5.0, le module requiert OpenFeign 10.1.0 ou supérieur. - Aucune compatibilité ascendante n'est garantie pour
feign-formau-delà de 3.5.0 avec OpenFeign inférieur à 10.*, en raison de refactorisations majeures dans OpenFeign 10.
En pratique, avec spring-cloud-openfeign, les versions antérieures à 2.0.3.RELEASE utilisent OpenFeign 9.*, tandis que les versions ultérieures optent pour OpenFeign 10.*. De plus, à partir de 2.0.3.RELEASE, spring-cloud-openfeign inclut déjà les dépendances feign-form adaptées, éliminant ainsi la nécessité de les spécifier manuellement. Notez que spring-cloud-starter-feign est obsolète et reste lié à OpenFeign 9.*.
Pour un projet utilisant Spring Cloud Greenwich.RELEACE, aucune dépendance supplémentaire n'est requise car le starter inclus gère les fichiers MultipartFile. Voici une implémentation type :
Configuration du client Feign
Créez une classe de configuration pour personnaliser l'encodeur Feign, en utilisant SpringFormEncoder pour supporter les formulaires multipart :
@Configuration
public class FeignMultipartConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverterFactory;
@Bean
@Primary
@Scope("prototype")
public Encoder formEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverterFactory));
}
}
Définition de l'interface Feign
Dans l'interface Feign, annotez les paramètres avec @RequestPart au lieu de @RequestParam, et spécifiez le type de consommation MEDIA_TYPE_MULTIPART :
@FeignClient(name = "file-service", configuration = FeignMultipartConfig.class)
public interface FileClient {
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
String uploadFile(@RequestPart("file") MultipartFile file, @RequestParam("label") String label);
}
Côté fournisseur de service
Le contrôleur côté serveur doit également utiliser @RequestPart pour le fichier et définir le type de contenu :
@RestController
public class FileServerController {
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String handleFileUpload(@RequestPart("file") MultipartFile uploadedFile, @RequestParam("label") String fileLabel) {
if (!uploadedFile.isEmpty()) {
try {
Path storagePath = Paths.get(System.getProperty("java.io.tmpdir"), uploadedFile.getOriginalFilename());
uploadedFile.transferTo(storagePath.toFile());
return uploadedFile.getOriginalFilename();
} catch (IOException e) {
throw new RuntimeException("Échec du stockage du fichier", e);
}
}
return "Aucun fichier fourni";
}
}
Consommation depuis un autre service
Un contrôleur client peut invoquer le service Feign de manière classique, en transmettant directement le MultipartFile reçu :
@RestController
public class ProxyController {
@Autowired
private FileClient fileClient;
@PostMapping("/proxy-upload")
public String proxyUpload(@RequestPart("file") MultipartFile file, @RequestParam("label") String label) {
return fileClient.uploadFile(file, label);
}
}
Cette configuration assure le bon fonctionnement des uploads via Feign dans un environnement Spring Cloud, à condition de respecter les alignements de versions entre OpenFeign et les modules form.