Feign 作为 Spring Cloud 中 RPC 工具,利用注解来描述接口,简化了 Java HTTP Client 的调用过程。但是在文件上传时,我们需要做一些额外的配置,不然会调用异常。
背景
我有一个服务 A,里面有个接口,需要接受上传的文件和一些参数,
@PostMapping({"/validityPeriod/update"})BizResponseproofValidityPeriodUpdate(@ValidatedValidityPeriodUpdateRequest request);
其中 ValidityPeriodUpdateRequest 代码如下:
publicclassValidityPeriodUpdateRequest{@NotEmpty(
message="操作名称 不能为空")@ApiModelProperty("操作名称")privateString operateName;@ApiModelProperty("延期时间,如: 2025-01-01")privateString delayDate;@ApiModelProperty("延期天数")privateInteger delayDays;@ApiModelProperty("延期数据, Excel 文件")privateMultipartFile delayData;@ApiModelProperty("备注")privateString remark;@NotEmpty(
message="操作人不能为空")@ApiModelProperty("操作人")privateString operator;
注意,private MultipartFile delayData;
接受一个文件。
然后服务B 作为一个 BFF,回去调用 服务 A 的 这个接口,
使用默认的 Feign 配置去调用的话
@FeignClient(name="ext-website-mkt-coupon-setting")publicinterfaceCouponOperateTaskClientextendsCouponOperateTaskService{}
会报如下异常:
org.springframework.http.converter.HttpMessageConversionException:Type definition error:[simple type,classjava.io.FileDescriptor]; nested exception iscom.fasterxml.jackson.databind.exc.InvalidDefinitionException:No serializer foundforclassjava.io.FileDescriptor and no properties discoveredtocreateBeanSerializer(toavoid exception, disableSerializationFeature.FAIL_ON_EMPTY_BEANS)(through reference chain:com.tuhu.mkt.coupon.api.dto.operate.request.ValidityPeriodUpdateRequest["delayData"]->org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile["inputStream"]->java.io.FileInputStream["fd"])
google 一下发现, openfign 不支持 参数中带文件。
解决方案
OpenFeign 默认不支持文件参数,但提供了feign-form 拓展工具。
查看官网介绍,大致意思如下:
feign-form 扩展依赖于 open-feign 的特定版本
open-feign 9.* 版本对应使用 feign-from 3.5.0 之前的版本
open-feign 10.1.0 以及以后的版本对应使用 3.5.0 以及以后版本
feign-from 3.5.0 之后的版本没有兼容 open-feign 10.* 之前的版本,10版本的 open-feign 重构了代码,所以最好的方法是用最新的版本
spring-cloud-openfeign:2.0.3.release 之前用 open-feign 9. 版本,2.0.3.release 之后的版本用 open-feign 10.*, 且在 2.0.3.release 之后的版本里面已经包含 feign-form 的依赖,不需要再去指定相应的版本*
spring-cloud-starter-feign 已经不推荐使用,他依赖的依然是 open-feign 9.*
好了看完 github 上的介绍,又看了下项目的 springcloud 版本 ,该版本不包含 feign-form 的依赖,需要我们手动引入 feign-from 3.5.0 之前的版本 版本。
添加 maven 依赖:
<dependency><groupId>io.github.openfeign.form</groupId><artifactId>feign-form</artifactId><version>3.4.1</version></dependency><dependency><groupId>io.github.openfeign.form</groupId><artifactId>feign-form-spring</artifactId><version>3.4.1</version></dependency>
引入完依赖后还需要编写配置类:
@ConfigurationpublicclassFeignSupportConfig{@AutowiredprivateObjectFactory<HttpMessageConverters> messageConverters;@Bean@Primary@Scope("prototype")publicEncoderfeignEncoder(){returnnewSpringFormEncoder(newSpringEncoder(messageConverters));}}
编写完 feign-form 的配置类后,还需要在 @FeignClient 注解中引入
@FeignClient(name="ext-website-mkt-coupon-setting", configuration=FeignSupportConfig.class)publicinterfaceCouponOperateTaskClientextendsCouponOperateTaskService{}
做完上面配置后,我们的代码写法也需要改动,
原来的接口定义:
@PostMapping({"/validityPeriod/update"})BizResponseproofValidityPeriodUpdate(@ValidatedValidityPeriodUpdateRequest request);
openfeign 也是不支持这样的类作为参数绑定的,需要结合@RequestPart
注解 和@RequestParam
注解,共同完成参数的绑定:
@PostMapping(value="/validityPeriod/update", consumes=MediaType.MULTIPART_FORM_DATA_VALUE)BizResponseproofValidityPeriodUpdate(@RequestPart("delayData")MultipartFile delayData,@RequestParam("operateName")String operateName,@RequestParam(name="delayDate",required=false)String delayDate,@RequestParam(name="delayDays",required=false)Integer delayDays,@RequestParam("remark")String remark,@RequestParam("operator")String operator);
注意,不要忘记加上consumes = MediaType.MULTIPART_FORM_DATA_VALUE
配置。
这样配置完之后,我们就再来测试下
完美解决问题。
总结
后面可以考虑升级 springcloud 版本来解决此问题。