上傳文件調用外部服務報錯:?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 }
方案二既支持文件上傳也支持實體類作為參數接收。
?