Feign远程调用丢失请求头

2023-09-18 16:30:18

前言

我们在写服务端项目的时候,总会限制对某些资源的访问,最常见的就是要求用户先登录才能访问资源,当用户登录后就会将此次会话信息保存进session,同时返回给浏览器指定的cookie键值,下次浏览器再次访问,请求头中就会携带这个cookie,我们也以次来识别用户的登录状态,做出正确响应。

一、问题

有时候,我们先行登录,然后访问服务A的某个方法,请求头中携带cookie,标识我们已经登录。但若是我们访问的目标方法在执行过程中使用feign进行远程调用服务B,而服务B也要先判断登录状态,我们可能发现服务B会调用失败,或者说拿不到数据,理由是服务B认为我们并未登录。而这时,如果我们直接从浏览器访问服务B的这个方法却能得到一个成功的响应。
在这里插入图片描述

二、查看源码

1、使用feign进行远程调用时,首先判断目标方法类型,如果是 toString(),hashCode(),equals()这几个方法,那就是本地直接完成了

在这里插入图片描述

2、执行真正的远程调用的方法

在这里插入图片描述

3、根据模板template来创建请求request(默认的情况feign是不会帮我们把原请求头参数复制到新请求的请求头上的),然后进行客户端client调用

在这里插入图片描述
4、但是创建请求时,会先调用所有的拦截器RequestInterceptors

在这里插入图片描述

5、所以我们可以自己向容器中注册一个拦截器RequestInterceptor,在这个拦截器中重写apply方法,在apply方法中把老请求的cookie复制到新request的请求头中,完成请求头的同步。(然后targetRequest 方法就会调用拦截器的apply方法,进行返回)

总结:

  • feign远程调用,自己创建一个新的request对象,按照指定的路径和参数发起新的请求,并得到响应结果。但是这个新的request对象请求头为空,所以丢失了原先请求中的数据。

  • feign在创建新的request对象时,会调用一系列容器中的RequestInterceptor对象,执行其apply方法,对这个创建好的request进行增强,再去真正执行请求。但是默认情况下容器中不存在这类拦截器对象。

  • 我们可以自己向容器中注册一个RequestInterceptor,在其apply方法体内,获取到原始request,将其数据取出,赋值到新的request中,完成请求头的同步。RequestContextHolder借助ThreadLocal将每一个原始请求与tomcat为其分配的线程绑定,之后,只要在同个线程内,随时随地都可轻易获取到原始request。而我们是在apply方法体内,通过 RequestContextHolder.getRequestAttributes() 获取的。RequestContextHolder是借助ThreadLocal将每一个原始请求与tomcat为其分配的线程绑定,之后,只要在同个线程内,随时随地都可轻易获取到原始request。

三、解决

在这里插入图片描述

1、同步时

直接向spring容器注入RequestInterceptor拦截器即可

/**
* Feign
* @return
*/
@Bean
public RequestInterceptor requestInterceptor(){
   return requestTemplate -> {
       //1、从RequestContextHolder获取原始请求的请求数据(请求参数、请求头等)
       ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
       HttpServletRequest request = attributes.getRequest();
       //2、同步请求头数据
       Enumeration<String> headerNames = request.getHeaderNames();
       if (headerNames != null) {
           while (headerNames.hasMoreElements()) {
               String name = headerNames.nextElement();
               String values = request.getHeader(name);
               requestTemplate.header(name, values);
           }
       }
   };
}

2、异步时

如果请求中使用了异步,也就是多线程,就算配置了上面的配置也会导致feign请求头丢失,因为请求头信息是通过threadLocal保存的,也就是只有在同一个线程中才能使用请求中的请求头并且同步初始请求头信息到feign请求中

解决办法

在异步调用时主动将请求头覆盖到异步线程的请求上下文中

// 通过请求上下文获取请求信息
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
    // 远程查询所有的收货地址列表
    // 在该线程中添加请求信息
    RequestContextHolder.setRequestAttributes(requestAttributes);
    List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
    orderConfirmVo.setAdress(address);
}, executor);CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
    // 远程查询购物车所有选中的购物想
    // 在该线程中添加请求信息
    RequestContextHolder.setRequestAttributes(requestAttributes);
    List<OrderItemVo> currentUserItems = cartFeignService.getCurrentUserItems();
    orderConfirmVo.setItems(currentUserItems);
}, executor);
更多推荐

LabVIEW开发基于物联网的多功能功率分析仪

LabVIEW开发基于物联网的多功能功率分析仪根据技术规则,电气元件网络中的单个被创建为在标称正弦波振动制造频率下运行。失真顺序的电流和电压波与正弦波不同,它们或多或少地扭曲成形状。它是由交流网络中非线性组件的存在引起的,例如静态转换器、旋转电气设备和电力变压器。由于其独特的特性,包含功率开关静态转换器的电流消费者是网

68、Spring Data JPA 的 方法名关键字查询(全自动,既不需要提供sql语句,也不需要提供方法体)

1、方法名关键字查询(全自动,既不需要提供sql语句,也不需要提供方法体)2、@Query查询(半自动:提供SQL或JPQL查询)3、自定义查询(全手动)★方法名关键字查询(全自动)(1)继承CrudRepository接口的DAO组件可按特定规则来定义查询方法,只要这些查询方法的方法名遵守特定的规则,SpringDa

如何进行性能测试

文章目录前言什么是性能测试为什么要做性能测试怎么做我们的性能测试SoloPiSoloPi的介绍和安装SoloPi的性能数据前言随着科学技术的迅速发展,信息时代离不开软件,软件的成功上线离不开软件测试的功劳,因此软件测试对于软件的重要性不言而喻。性能测试作为软件测试中的一部分,通过自动化测试工具模拟多种正常、峰值以及异常

Flutter实现地图上汇聚到一点的效果。

要求效果:实现的效果:代码:选择点的界面:import'dart:math';import'package:flutter/material.dart';import'package:get/get.dart';import'package:kq_flutter_widgets/widgets/animate/mapC

hive基于新浪微博的日志数据分析——项目及源码

有需要本项目的全套资源资源以及部署服务可以私信博主!!!该系统的目的是利用大数据技术,分析新浪微博的日志数据,从而探索用户行为、内容传播和移动设备等各个层面的特性和动向。这项研究为公司和个人在制定营销战略、设计产品和提供用户服务时,提供了有价值的参考和辅助。利用Hive平台,该系统可以处理和分析大量的微博数据,为我们提

数据治理-EDRM电子取证

EDRM是电子取证标准和指南的组织,该框架提供了一种电子取证的方法,对于涉及确定相关内部数据的存储方式和位置、适用什么保留策略、哪些数据不可访问以及哪些工具可用于协助识别流程的人员来说,这种方法非常方便。EDRM模型假定数据或信息治理已到位。该模型包括8个可以迭代的电子取证阶段,随着电子取证的发展,可取证的数据和信息的

C++:string类的常用接口说明及其模拟实现

本文主要介绍string类和该类常用的接口,并根据接口功能对其进行模拟实现。目录一、string类的常用接口说明1.string类对象的常见构造2.string类对象的容量操作3.string类对象的访问及遍历操作4.string类对象的修改操作5.string类非成员函数二、string的模拟实现1.默认成员函数和成

【CDN和UDN】CDN和UDN技术特点以及使用场景

内容分发网络(CDN)和用户自定义网络(UDN)是两种不同的网络技术,在选择时,往往不能准备把握具不同的技术特点和应用场景。CDN主要用于加速内容分发,而UDN则主要用于支持用户自定义的网络需求。本文简要介绍下内容分发网络(CDN):一种通过在多个地理位置部署缓存服务器来加速内容分发的网络技术。当用户请求内容时,CDN

【Seata】05 - Seata Saga 模式简单整理、Docker 部署 Nacos 单机(基于 Jpom)相关配置

文章目录前言参考目录Saga模式知识点简单整理1、适用场景、优缺点2、Saga模式的使用3、可能出现的问题以及解决方法Docker部署Nacos单机(基于Jpom)步骤1:拉取镜像步骤2:构建容器步骤3:Nacos设置Seata配置文件步骤4:修改SeataServer相关配置步骤5:修改SeataClient相关配置

@Autowire、@Recourse用啥?

在使用IDEA写Spring相关的项目的时候,在字段上使用@Autowired注解时,总是会有一个波浪线提示:Fieldinjectionisnotrecommended.这是为啥呢?今天就来一探究竟。众所周知,在Spring里面有三种可选的注入方式:构造器注入、Setter方法注入、Field注入,我们先来看下这三种

设计模式:外观模式(C++实现)

外观模式(FacadePattern)是一种结构设计模式,它提供了一个统一的接口,用于访问子系统中的一组接口。外观模式隐藏了子系统的复杂性,使得客户端只需要与外观对象交互,而不需要直接与子系统中的对象进行交互。以下是一个简单的C++外观模式的示例:#include<iostream>//子系统类AclassSubsys

热文推荐