feign微服務(wù)之間傳遞請求頭數(shù)據(jù)方式
直接在微服務(wù)遠(yuǎn)程調(diào)用中獲取請求頭數(shù)據(jù)不能直接獲取到.為什么? 看源碼
默認(rèn)情況下feign遠(yuǎn)程調(diào)用的時候不會傳遞請求頭!
遠(yuǎn)程調(diào)用源碼:

每一次遠(yuǎn)程請求的時候都創(chuàng)建了一個新的Request Template對象,在該對象中不包含之前的請求頭數(shù)據(jù)
解決方案
方案一
在feign接口上添加對應(yīng)的形式參數(shù)即可
弊端:每一個接口想要獲取參數(shù)都需要在接口方法上添加對應(yīng)的形式參數(shù).影響代碼效率
方案二
使用OpenFeign中的攔截器(RequestInterceptor)來攔截請求,添加請求頭。
方案一演示
FeignClient接口:
@FeignClient(value = "service-cart",fallback = FeignClientFallback.class)//降級方法
public interface FeignClient {
public Result xxx(
@RequestParam(value = "xx" )Long xx ,
@RequestParam(value = "xx" )String xx);
}Controller遠(yuǎn)程接口:
@RestController
@RequestMapping("/x/x/x")
public class Controller {
@GetMapping("/x/x/x")
public Result aaa(
@RequestParam(value = "xx" )Long xx ,
@RequestParam(value = "xx" )String xx
) {
return Result.ok() ;
}
}
//@RequestParam 通過傳參的方法傳遞過去,并非從請求頭上拿FeignClient.aaa(xx,xx); //遠(yuǎn)程調(diào)用時候傳參
方案二演示
思路:在OpenFeign遠(yuǎn)程調(diào)用 定義一個攔截器類實現(xiàn)(RequestInterceptor)接口給RequestTemplate設(shè)置上請求頭
核心源碼:
// feign.SynchronousMethodHandler#executeAndDecode
Request targetRequest(RequestTemplate template) {
// 在構(gòu)建目標(biāo)請求對象的時候,遍歷所有的攔截器,
// 然后調(diào)用了apply方法把RequestTemplate作為參數(shù)傳遞過去
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(template);
}攔截器類如何獲取Controller請求頭數(shù)據(jù)呢?
- 思路1:通過Map實現(xiàn)
- 原理:feign的調(diào)用鏈?zhǔn)褂玫氖峭粋€線程對象
- 思路2:通過ThreadLocal對象在一個線程中共享HttpServletRequest對象(使用JDK自帶的ThreadLocal在一個線程中共享HttpServletRequest對象,原理就是在底層維護(hù)了一個Map。)
- 思路3: 使用spring提供的對象RequestContextHolder進(jìn)行實現(xiàn)!
原理:底層通過RequestContextListener監(jiān)聽器實現(xiàn)
思路1實現(xiàn):
- 獲取Controller中請求頭思路:
- feign的調(diào)用鏈?zhǔn)褂玫氖峭粋€線程對象
- 所以可以在Controller中定義一個Map集合,鍵就是當(dāng)前線程對象,值就是HttpServletRequest對象
//定義一個Map,作用是在tController和FeignClientInterceptor(攔截器)中共享HttpServletRequest
public static final ConcurrentHashMap<Thread, HttpServletRequest> concurrentHashMap =new ConcurrentHashMap<>();
//給map中存request對象
@GetMapping("/xx")//required:傳遞過來的參數(shù)可以有可以無
public String addCart(HttpServletRequest request, ) {
concurrentHashMap.put(Thread.currentThread(),request);
}攔截器類通過map獲取請求頭數(shù)據(jù),設(shè)置給RequestTemplate
@Component
@Slf4j
public class FeignClientInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
log.info("FeignClientInterceptor..........");
HttpServletRequest httpServletRequest = CartController.concurrentHashMap.get(Thread.currentThread());
//給requestTemplate 添加上對應(yīng)的請求頭中的數(shù)據(jù):aa,bbb
requestTemplate.header("userId",httpServletRequest.getHeader("aa"));
requestTemplate.header("userTempId",httpServletRequest.getHeader("bbb"));
}
}思路2實現(xiàn):
在Controller中定義一個ThreadLocal對象
public static final ThreadLocal<HttpServletRequest> threadLocal = new ThreadLocal<>();
然后在方法中調(diào)用set方法添加數(shù)據(jù)(request)
threadLocal.set(request);
在攔截器中調(diào)用get方法就可以拿到HttpServletRequest對象,然后給requestTemplate 添加上對應(yīng)的請求頭中的數(shù)據(jù):aa,bbb
HttpServletRequest httpServletRequest = CartController.threadLocal.get();
requestTemplate.header("userId",httpServletRequest.getHeader("aa"));
requestTemplate.header("userTempId",httpServletRequest.getHeader("bbb"));思路3實現(xiàn):使用spring提供的對象RequestContextHolder直接在攔截器類獲取request對象
//RequestContextHolder對象中獲取一個線程內(nèi)所共享的HttpServletRequest對象
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
//獲取到了request對象剩下的同上思路3的在Controller使用模塊中通過RequestContextHolder隱式獲取請求頭數(shù)據(jù)
直接在使用模塊獲取ServletRequestAttributes對象,不需要在參數(shù)添加@RequestHeader(value = "xxx")
因為剛才已經(jīng)用到了request對象
// 獲取ServletRequestAttributes對象
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest requestAttributesRequest = requestAttributes.getRequest();
String userId = requestAttributesRequest.getHeader("userId");
String userTempId = requestAttributesRequest.getHeader("userTempId") ;代碼優(yōu)化:
1.將從RequestContextHolder對象中讀取xx和xx數(shù)據(jù)的代碼封裝到一個工具類(utilxx),并使用一個實體類返回相關(guān)數(shù)據(jù):
public class Utils {
public static UserInfoVo userInfo() {
// 獲取請求對象
ServletRequestAttributes requestAttributes
= (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
// 判斷requestAttributes是否為空
if(requestAttributes != null) {
// 獲取request對象
HttpServletRequest request = requestAttributes.getRequest();
// 從請求對象中獲取xx和xx數(shù)據(jù)
String userid = request.getHeader("id");
String username = request.getHeader("name");
// 封裝數(shù)據(jù)到xxx對象中
User user = new user() ;
if(!StringUtils.isEmpty(userId)) {
userAuthInfoVo.setUserId(Long.parseLong(userId));
}
userAuthInfoVo.setUserTempId(username);
// 返回
return user ;
}
return null ;
}
}2.FeignClientInterceptor抽取成公共組件
為了實現(xiàn)FeignClientInterceptor的復(fù)用,那么此時可以將FeignClientInterceptor抽取成一個公共的組件
@Slf4j
@Component
public class FeignClientInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
log.info("FeignClientInterceptor攔截器執(zhí)行了....");
User user = User.getUser();
if(user != null) {
Long userId = user.getUserId();
if(userId != null) {
template.header("userId" , String.valueOf(userId)) ;
}
template.header("username" , userAuthInfo.getUsername()) ;
}
}
}自定義注解:
因為抽取公共類后包路徑不一樣會導(dǎo)致掃描不到所以,可以使用自定義注解或@Import解決,這里使用自定義注解
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@Import(value = EnableFeignClientInterceptor.class)
public @interface EnableFeignClientInterceptor {
}啟動類加上@EnableFeignClientIntercepto即可使用
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- Spring Cloud之遠(yuǎn)程調(diào)用OpenFeign參數(shù)傳遞
- SpringCloud OpenFeign 參數(shù)傳遞和響應(yīng)處理的詳細(xì)步驟
- Spring?cloud?OpenFeign中動態(tài)URl、動態(tài)傳遞接口地址代碼示例
- 解決feignclient調(diào)用服務(wù),傳遞的中文數(shù)據(jù)成???問題
- Springcloud中Feign傳遞參數(shù)的過程解析
- SpringCloud Feign傳遞HttpServletRequest對象流程
- SpringBoot如何通過Feign調(diào)用傳遞Header中參數(shù)
相關(guān)文章
使用java代碼獲取新浪微博應(yīng)用的access token代碼實例
這篇文章主要介紹了使用java代碼獲取新浪微博應(yīng)用的access token實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
基于spring-security 401 403錯誤自定義處理方案
這篇文章主要介紹了基于spring-security 401 403錯誤自定義處理方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
Java實現(xiàn)并發(fā)執(zhí)行定時任務(wù)并手動控制開始結(jié)束
這篇文章主要介紹了Java實現(xiàn)并發(fā)執(zhí)行定時任務(wù)并手動控制開始結(jié)束,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05
利用Kafka動態(tài)調(diào)整topic分區(qū)partition
這篇文章主要介紹了利用Kafka動態(tài)調(diào)整topic分區(qū)partition問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12

