使用OpenFeign遠程調用時請求頭處理報錯問題


1. 錯誤信息


basic.result.exception.OtherException: feign error:系統異常:Content type 'multipart/form-data;boundary=--------------------------679449061975336133574827;charset=UTF-8' not supported.請聯系管理員
	at com.assembly.oauth.web.exception.auth.FeignErrorDecoder.decode(FeignErrorDecoder.java:26)
	at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:149)
	at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:78)
	at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
	at com.sun.proxy.$Proxy191.addStationModelList(Unknown Source)
	at com.assembly.dg.online.controller.TestController.importStation(TestController.java:326)
	at com.assembly.dg.online.controller.TestController$$FastClassBySpringCGLIB$$1.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:684)
	at com.assembly.dg.online.controller.TestController$$EnhancerBySpringCGLIB$$1.importStation(<generated>)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:117)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:106)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at com.assembly.oauth.web.authorization.interceptor.CrossFilter.doFilter(CrossFilter.java:57)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
	at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:96)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:41002)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:668)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

2. 使用場景以及問題出現的原因

在本地微服務中使用Spring MVC 上傳文件時, 因為使用了MultipartFile 對象接受, 所以前端設置 請求頭"Content-type"為multipart/form-data; ,這在本地 微服務中是沒有問題的,

但是在處理請求的過程中, 使用了OpenFeign 遠程調用了 其他的微服務保存信息, 就有問題了,

@RequestMapping(value = "importStation")
    @NoAuthorization
    public void importStation(MultipartFile file, HttpServletRequest request) throws IOException {

        // ... 業務處理
        // RPC遠程調用 保存信息
        dgRpcRemote.addStationModelList(stationModelList);
      
    }

本項目中使用的是OpenFeign進行遠程調用, 為了在調用時,可以攜帶調用方的請求頭信息,例如token等等, 項目中自定義了一個攔截器進行處理:

@Component
public class FeignBasicAuthRequestInterceptor implements RequestInterceptor {
    public FeignBasicAuthRequestInterceptor() {
    }

    public void apply(RequestTemplate requestTemplate) {
        try {
            //獲取 ThreadLocal中的 本地Request對象
            ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return;
            }

            HttpServletRequest request = attributes.getRequest();
            Enumeration<String> headerNames = request.getHeaderNames();
            String type;
            String tenantCode;
            if (headerNames != null) {
                // 全盤拷貝過去
                while(headerNames.hasMoreElements()) {
                    type = (String)headerNames.nextElement();
                    tenantCode = request.getHeader(type);
                    requestTemplate.header(type, new String[]{tenantCode});
                }
            }

            type = TenantContextHolder.getAuthorizationType();
            if (StringUtils.isNotBlank(type)) {
                requestTemplate.header("tenantAuthorization", new String[]{type});
            }

            tenantCode = TenantContextHolder.getTenantCode();
            if (StringUtils.isNotBlank(tenantCode)) {
                requestTemplate.header("tenantCode", new String[]{tenantCode});
            }

            String tokenId = TenantContextHolder.getTokenId();
            if (StringUtils.isNotBlank(tokenId)) {
                requestTemplate.header("tenantToken", new String[]{tokenId});
            }

            requestTemplate.header("Content-Type", new String[]{"application/json;charset=UTF-8"});
        } catch (Exception var8) {
            var8.printStackTrace();
        }

    }
}

只需實現 接口RequestInterceptor 並被Spring掃描到即可,OpenFeign會自動調用,此類重寫的 apply 方法中就 將本地的請求Request對象的請求頭全部拷貝到 新的請求中用於調用,這樣multipart/form-data; 也就拷貝了過去,但是在RPC 調用的接受方,確只是普通的 Rest 接口, 需要的是application/json ,所以就報了上述錯誤.

如果項目中在使用Feign進行遠程調用,並有需要進行請求頭傳遞的需求,可以參考此方式

3. 解決方式

因為在攔截器中獲取的是 ThreadLocal 中保存的本地 請求Request 信息,所以 只需保證 在調用時不在請求所在線程即可:

new Thread(() ->dgRpcRemote.addStationModelList(stationModelList) ).start();

在調用時另啟動一個線程進行調用即可, 如果有更好的方式,歡迎指教


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM