最近项目转型使用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.spring[email protected]65669249: startup date [Sat Dec 22 03:42:37 CST 2018]; parent: org.springframework.boot[email protected]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.springf[email protected]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个系统默认注解处理器。
至此,重新编译,运行,工作正常。问题解决。
原文地址:https://www.cnblogs.com/oilamp/p/10159741.html
时间: 2024-09-30 05:43:13