zoukankan      html  css  js  c++  java
  • 使用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();
    

    在调用时另启动一个线程进行调用即可, 如果有更好的方式,欢迎指教

  • 相关阅读:
    IronPython初体验
    HOWTO: 部署时附带安装MSDE
    翻译:FileSystemWatcher Tips
    再见了 母校!
    招聘.net开发工程师(2名)和ASP开发工程师(1名)工作地点:北京
    年终感言提纲
    可以不讲,但不可以不懂:英语实用脏话精选
    一个混乱的时期
    异步消息的传递-回调机制
    搞c++的 大家看看
  • 原文地址:https://www.cnblogs.com/xjwhaha/p/14244828.html
Copyright © 2011-2022 走看看