SpringCloud+Feign环境下文件上传与form-data同时存在的解决办法

最近项目转型使用SpringCloud框架下的微服务架构,各微服务之间使用Feign进行调用。期间,发现若被调用方法涉及到文件上传且仅存在单个文件时,一切正常,代码片段如下:

1     @RequestMapping(value = "/if/****/add", method = RequestMethod.POST,consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
2     JSONObject add(@RequestPart(value = "file") MultipartFile file);

但若同时需要传递其他form-data数据时,则一直报错。

1     @RequestMapping(value = "/if/****", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
2     Object add(@RequestParam(name = "objectCode") String objectCode, @RequestPart(name = "file") MultipartFile file);

报错信息为:

 1 org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
 2     at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:193)
 3     at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:109)
 4     at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
 5     at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158)
 6     at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
 7     at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
 8     at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
 9     at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
10     at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
11     at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
12     at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
13     at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
14     at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
15     at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
16     at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
17     at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
18     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
19     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
20     at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
21     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
22     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
23     at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
24     at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
25     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
26     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)

此时,feign日志为:

 1 2018-12-22 03:42:37.591 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] ---> POST http://assembly-uiif/if/test1/public/t1?objectCode=test%3ASat+Dec+22+03%3A42%3A31+CST+2018 HTTP/1.1
 2 2018-12-22 03:42:37.593 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] Content-Type: multipart/form-data; charset=UTF-8; boundary=167d24a8200
 3 2018-12-22 03:42:37.594 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] Content-Length: 161
 4 2018-12-22 03:42:37.595 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] 
 5 2018-12-22 03:42:37.596 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] Binary data
 6 2018-12-22 03:42:37.599 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] ---> END HTTP (161-byte body)
 7 2018-12-22 03:42:37.618  INFO 66057 --- [io-31023-exec-2] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@65669249: startup date [Sat Dec 22 03:42:37 CST 2018]; parent: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@47df5041
 8 2018-12-22 03:42:37.777  INFO 66057 --- [io-31023-exec-2] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
 9 2018-12-22 03:42:38.615  INFO 66057 --- [io-31023-exec-2] c.netflix.config.ChainedDynamicProperty  : Flipping property: assembly-uiif.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
10 2018-12-22 03:42:38.677  INFO 66057 --- [io-31023-exec-2] c.n.u.concurrent.ShutdownEnabledTimer    : Shutdown hook installed for: NFLoadBalancer-PingTimer-assembly-uiif
11 2018-12-22 03:42:38.786  INFO 66057 --- [io-31023-exec-2] c.netflix.loadbalancer.BaseLoadBalancer  : Client: assembly-uiif instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=assembly-uiif,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
12 2018-12-22 03:42:38.796  INFO 66057 --- [io-31023-exec-2] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
13 2018-12-22 03:42:38.860  INFO 66057 --- [io-31023-exec-2] c.netflix.config.ChainedDynamicProperty  : Flipping property: assembly-uiif.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
14 2018-12-22 03:42:38.863  INFO 66057 --- [io-31023-exec-2] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client assembly-epsic-uiif initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=assembly-uiif,current list of Servers=[192.168.43.31:20008],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone;    Instance count:1;    Active connections count: 0;    Circuit breaker tripped count: 0;    Active connections per server: 0.0;]
15 },Server stats: [[Server:192.168.43.31:20008;    Zone:defaultZone;    Total Requests:0;    Successive connection failure:0;    Total blackout seconds:0;    Last connection made:Thu Jan 01 08:00:00 CST 1970;    First connection made: Thu Jan 01 08:00:00 CST 1970;    Active Connections:0;    total failure count in last (1000) msecs:0;    average resp time:0.0;    90 percentile resp time:0.0;    95 percentile resp time:0.0;    min resp time:0.0;    max resp time:0.0;    stddev resp time:0.0]
16 ]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@114d8fc3
17 2018-12-22 03:42:39.606 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] <--- HTTP/1.1 200 (2006ms)
18 2018-12-22 03:42:39.607 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] cache-control: no-cache, no-store, max-age=0, must-revalidate
19 2018-12-22 03:42:39.608 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] content-type: application/json;charset=UTF-8
20 2018-12-22 03:42:39.608 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] date: Fri, 21 Dec 2018 19:42:39 GMT
21 2018-12-22 03:42:39.609 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] expires: 0
22 2018-12-22 03:42:39.610 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] pragma: no-cache
23 2018-12-22 03:42:39.610 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] transfer-encoding: chunked
24 2018-12-22 03:42:39.610 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] x-application-context: assembly-uiif:develop:20008
25 2018-12-22 03:42:39.610 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] x-content-type-options: nosniff
26 2018-12-22 03:42:39.613 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] x-frame-options: SAMEORIGIN
27 2018-12-22 03:42:39.613 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] x-xss-protection: 1; mode=block
28 2018-12-22 03:42:39.613 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] 
29 2018-12-22 03:42:39.615 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] {"head":{"orgId":0,"orgCode":"","vers":null,"reqId":null,"errorCode":1,"errorCodeI18n":"i18n.errorCode.1","errorMessage":"Required request part 'file' is not present","errorStack":"org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present\n\tat org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:193)\n\tat org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:109)\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:661)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:742)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat com.egoonet.devtools.springstarter.iam.sso.config.web.MyFilterSecurityInterceptor.invoke(MyFilterSecurityInterceptor.java:61)\n\tat com.egoonet.devtools.springstarter.iam.sso.config.web.MyFilterSecurityInterceptor.doFilter(MyFilterSecurityInterceptor.java:31)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat com.***.web.MyFilterSecurityInterceptor.invoke(MyFilterSecurityInterceptor.java:61)\n\tat com.***.web.MyFilterSecurityInterceptor.doFilter(MyFilterSecurityInterceptor.java:31)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:150)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)\n\tat org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)\n\tat org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)\n\tat org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.lang.Thread.run(Thread.java:748)\n","userId":0,"appId":108},"info":{"now":"Sat Dec 22 03:42:39 CST 2018"}}
30 2018-12-22 03:42:39.615 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] <--- END HTTP (12082-byte body)

很显然,feign发送的报文不对,正常的报文应该是:

 1 POST /if/test1/public/t1 HTTP/1.1
 2 Host: localhost:20008
 3 cache-control: no-cache
 4 Postman-Token: 1c879cba-fdf3-4b9a-b1ef-e3d5af7be37e
 5 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
 6 
 7 Content-Disposition: form-data; name="objectCode"
 8 
 9 abcd
10 
11 Content-Disposition: form-data; name="file"; filename="/Users/***/Downloads/03.jpg
12 
13 
14 ------WebKitFormBoundary7MA4YWxkTrZu0gW--

ok,到此可知,问题出在feign发送报文时发送错误。

查阅feign源码,参考网络资料,实现一个自定义注解:

 1 /**
 2  * Feign代理方法上,与 RequestPart 一起组成多参数模式的注解
 3  */
 4 @Target(ElementType.PARAMETER)
 5 @Retention(RetentionPolicy.RUNTIME)
 6 @Documented
 7 public @interface RequestPartParam {
 8 
 9     String name() default "";
10 
11     boolean required() default true;
12 
13 }

实现自定义注解处理器:

 1 import feign.MethodMetadata;
 2 import org.springframework.cloud.netflix.feign.AnnotatedParameterProcessor;
 3 
 4 import java.lang.annotation.Annotation;
 5 import java.lang.reflect.Method;
 6 import java.util.Map;
 7 
 8 import static feign.Util.checkState;
 9 import static feign.Util.emptyToNull;
10 
11 /**
12  *
13  */
14 public class RequestPartParamParameterProcessor implements AnnotatedParameterProcessor {
15 
16     private static final Class<RequestPartParam> ANNOTATION = RequestPartParam.class;
17 
18     @Override
19     public Class<? extends Annotation> getAnnotationType() {
20         return ANNOTATION;
21     }
22 
23     @Override
24     public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
25         int parameterIndex = context.getParameterIndex();
26         Class<?> parameterType = method.getParameterTypes()[parameterIndex];
27         MethodMetadata data = context.getMethodMetadata();
28 
29         if (Map.class.isAssignableFrom(parameterType)) {
30             checkState(data.queryMapIndex() == null, "Query map can only be present once.");
31             data.queryMapIndex(parameterIndex);
32 
33             return true;
34         }
35 
36         RequestPartParam requestPartParam = ANNOTATION.cast(annotation);
37         String name = requestPartParam.name();
38         checkState(emptyToNull(name) != null,
39                 "RequestPartParam.name() was empty on parameter %s",
40                 parameterIndex);
41         context.setParameterName(name);
42 
43         data.formParams().add(name);
44 
45         return true;
46     }
47 
48 }

实现自定义的编码器:

 1 import feign.RequestTemplate;
 2 import feign.codec.EncodeException;
 3 import feign.codec.Encoder;
 4 import feign.form.FormEncoder;
 5 import feign.form.MultipartFormContentProcessor;
 6 import feign.form.spring.SpringManyMultipartFilesWriter;
 7 import feign.form.spring.SpringSingleMultipartFileWriter;
 8 import lombok.val;
 9 import org.springframework.web.multipart.MultipartFile;
10 
11 import java.lang.reflect.ParameterizedType;
12 import java.lang.reflect.Type;
13 import java.util.HashSet;
14 import java.util.Map;
15 import java.util.Set;
16 
17 import static feign.form.ContentType.MULTIPART;
18 import static java.util.Collections.singletonMap;
19 
20 /**
21  *
22  */
23 public class FeignSpringFormEncoder extends FormEncoder {
24 
25     public FeignSpringFormEncoder () {
26         this(new Encoder.Default());
27     }
28 
29     public FeignSpringFormEncoder (Encoder delegate) {
30         super(delegate);
31 
32         val processor = (MultipartFormContentProcessor) getContentProcessor(MULTIPART);
33         processor.addWriter(new SpringSingleMultipartFileWriter());
34         processor.addWriter(new SpringManyMultipartFilesWriter());
35     }
36 
37     @Override
38     public void encode (Object object, Type bodyType, RequestTemplate template) throws EncodeException {
39         if ((bodyType instanceof ParameterizedType) && ((ParameterizedType) bodyType).getRawType().equals(Map.class)) {
40             val data = (Map<String, Object>) object;
41             Set<String> nullSet = new HashSet<>();
42             for (Map.Entry<String, Object> entry : data.entrySet()) {
43                 if (entry.getValue() == null) {
44                     nullSet.add(entry.getKey());
45                 }
46             }
47             for (String s : nullSet) {
48                 data.remove(s);
49             }
50             super.encode(data, MAP_STRING_WILDCARD, template);
51             return;
52         } else if (bodyType.equals(MultipartFile.class)) {
53             val file = (MultipartFile) object;
54             val data = singletonMap(file.getName(), object);
55             super.encode(data, MAP_STRING_WILDCARD, template);
56             return;
57         } else if (bodyType.equals(MultipartFile[].class)) {
58             val file = (MultipartFile[]) object;
59             if (file != null) {
60                 val data = singletonMap(file.length == 0 ? "" : file[0].getName(), object);
61                 super.encode(data, MAP_STRING_WILDCARD, template);
62                 return;
63             }
64         }
65         super.encode(object, bodyType, template);
66     }
67 
68 }

定义bean:

 1     @Bean
 2     public RequestPartParamParameterProcessor requestPartParamParameterProcessor() {
 3         return new RequestPartParamParameterProcessor();
 4     }
 5 
 6     @Bean
 7     public PathVariableParameterProcessor pathVariableParameterProcessor() {
 8         return new PathVariableParameterProcessor();
 9     }
10 
11     @Bean
12     public RequestParamParameterProcessor requestParamParameterProcessor() {
13         return new RequestParamParameterProcessor();
14     }
15 
16     @Bean
17     public RequestHeaderParameterProcessor requestHeaderParameterProcessor() {
18         return new RequestHeaderParameterProcessor();
19     }

注意,参阅  org.springframework.cloud.netflix.feign.support.SpringMvcContract 中的代码可知,使用自定义注解处理器时,必须自行处理另外3个系统默认注解处理器。

至此,重新编译,运行,工作正常。问题解决。

猜你喜欢

转载自www.cnblogs.com/oilamp/p/10159741.html