spring cloud feign 上传文件报not a type supported by this encoder解决方案
上传文件调用外部服务报错: not a type supported by this encoder
查看SpringFormEncoder类的源码:
1 public class SpringFormEncoder extends FormEncoder 2 { 3 4 public SpringFormEncoder() 5 { 6 this(((Encoder) (new feign.codec.Encoder.Default()))); 7 } 8 9 public SpringFormEncoder(Encoder delegate) 10 { 11 super(delegate);//调用父类的构造方法 12 MultipartFormContentProcessor processor = (MultipartFormContentProcessor)getContentProcessor(ContentType.MULTIPART); 13 processor.addWriter(new SpringSingleMultipartFileWriter()); 14 processor.addWriter(new SpringManyMultipartFilesWriter()); 15 } 16 17 public void encode(Object object, Type bodyType, RequestTemplate template) 18 throws EncodeException 19 { 20 if(!bodyType.equals(org/springframework/web/multipart/MultipartFile)) 21 { 22 super.encode(object, bodyType, template);//调用FormEncoder对应方法 23 return; 24 } else 25 { 26 MultipartFile file = (MultipartFile)object; 27 java.util.Map data = Collections.singletonMap(file.getName(), object); 28 super.encode(data, MAP_STRING_WILDCARD, template); 29 return; 30 } 31 } 32 }
可以发现SpringFormEncoder的encode方法当传送的对象不是MultipartFile的时候,就会调用super.encode, 也就是FormEncoder的encode方法。
FormEncoder类的部分源码:
1 public FormEncoder() 2 { 3 this(((Encoder) (new feign.codec.Encoder.Default()))); 4 } 5 6 public FormEncoder(Encoder delegate) 7 { 8 _flddelegate = delegate; 9 List list = Arrays.asList(new ContentProcessor[] { 10 new MultipartFormContentProcessor(delegate), new UrlencodedFormContentProcessor() 11 }); 12 processors = new HashMap(list.size(), 1.0F); 13 ContentProcessor processor; 14 for(Iterator iterator = list.iterator(); iterator.hasNext(); processors.put(processor.getSupportedContentType(), processor)) 15 processor = (ContentProcessor)iterator.next(); 16 17 } 18 19 public void encode(Object object, Type bodyType, RequestTemplate template) 20 throws EncodeException 21 { 22 String contentTypeValue = getContentTypeValue(template.headers());//这里会去到@PostMapping中consumes的值,所以参数需要传对象时指定一下consumes 23 ContentType contentType = ContentType.of(contentTypeValue);//为啥指定consumes,是因为不指定就是application/x-www-form-urlencoded,而且processors中也包含,为啥包含见FormEncoder的构造函数 24 if(!MAP_STRING_WILDCARD.equals(bodyType) || !processors.containsKey(contentType)) 25 { 26 _flddelegate.encode(object, bodyType, template);//_flddelegate是啥呢,是SpringFormEncoder传递过来,也就是new Encoder.Default() 27 return; 28 } 29 Charset charset = getCharset(contentTypeValue); 30 Map data = (Map)object; 31 try 32 { 33 ((ContentProcessor)processors.get(contentType)).process(template, charset, data); 34 } 35 catch(Exception ex) 36 { 37 throw new EncodeException(ex.getMessage()); 38 } 39 }
FormEncoderr的encode方法当传送的对象是json格式的字符串的时候,就会调用 _flddelegate.encode,即Encoder.Default的encode方法,而这个Encoder.Default的encode方法判断传送的类型不是String或者byte[],就会抛异常
1 public interface Encoder 2 { 3 public static class Default 4 implements Encoder 5 { 6 7 public void encode(Object object, Type bodyType, RequestTemplate template) 8 { 9 if(bodyType == java/lang/String) 10 template.body(object.toString()); 11 else 12 if(bodyType == [B) 13 template.body((byte[])(byte[])object, null); 14 else 15 if(object != null)//当我们用对象传递参数的时候,会走这里 16 throw new EncodeException(String.format("%s is not a type supported by this encoder.", new Object[] { 17 object.getClass() 18 })); 19 } 20 21 public Default() 22 { 23 } 24 } 25 26 27 public abstract void encode(Object obj, Type type, RequestTemplate requesttemplate) 28 throws EncodeException; 29 30 public static final Type MAP_STRING_WILDCARD = Util.MAP_STRING_WILDCARD; 31 32 }
解决方案一:继续使用前面提到的方案,如果引用该配置类的FeignClient中,没有使用实体类作为参数的接口,则去掉配置类上的注解@Configuration就可以了,去掉注解@Configuration之后,该配置就只对通过configuration属性引用该配置的FeignClient起作用(或者将该文件上传接口单独放到一个FeignClient中,去掉配置类上的注解@Configuration)。
方案一只支持文件上传,如果引用该配置的FeignClient中有使用实体类作为参数接收的接口,则调用该接口时会抛异常。
解决方案二:继续使用前面提到的方案,将配置文件修改为如下:
1 @Configuration 2 class MultipartSupportConfig { 3 @Autowired 4 private ObjectFactory<HttpMessageConverters> messageConverters; 5 6 @Bean 7 public Encoder feignFormEncoder() { 8 return new SpringFormEncoder(new SpringEncoder(messageConverters)); 9 } 10 }
方案二既支持文件上传也支持实体类作为参数接收。