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

每一次遠程請求的時候都創(chuàng)建了一個新的Request Template對象,在該對象中不包含之前的請求頭數(shù)據(jù)
解決方案:
方案一:在feign接口上添加對應的形式參數(shù)即可
弊端:每一個接口想要獲取參數(shù)都需要在接口方法上添加對應的形式參數(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遠程接口:
@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); //遠程調(diào)用時候傳參
方案二演示:
思路:在OpenFeign遠程調(diào)用 定義一個攔截器類實現(xiàn)(RequestInterceptor)接口給RequestTemplate設(shè)置上請求頭
核心源碼:
// feign.SynchronousMethodHandler#executeAndDecode
Request targetRequest(RequestTemplate template) {
// 在構(gòu)建目標請求對象的時候,遍歷所有的攔截器,
? ? //? ?然后調(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)用鏈使用的是同一個線程對象
思路2:通過ThreadLocal對象在一個線程中共享HttpServletRequest對象(使用JDK自帶的ThreadLocal在一個線程中共享HttpServletRequest對象,原理就是在底層維護了一個Map。)
思路3: 使用spring提供的對象RequestContextHolder進行實現(xiàn)!
原理:底層通過RequestContextListener監(jiān)聽器實現(xiàn)
思路1實現(xiàn):
獲取Controller中請求頭思路:
feign的調(diào)用鏈使用的是同一個線程對象
所以可以在Controller中定義一個Map集合,鍵就是當前線程對象,值就是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 添加上對應的請求頭中的數(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 添加上對應的請求頭中的數(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的復用,那么此時可以將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()) ;
}
}
}
自定義注解:
因為抽取公共類后包路徑不一樣會導致掃描不到所以,可以使用自定義注解或@Import解決,這里使用自定義注解文章來源:http://www.zghlxwxcb.cn/news/detail-455228.html
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@Import(value = EnableFeignClientInterceptor.class)
public @interface EnableFeignClientInterceptor {
}
啟動類加上@EnableFeignClientIntercepto即可使用文章來源地址http://www.zghlxwxcb.cn/news/detail-455228.html
到了這里,關(guān)于feign微服務之間傳遞請求頭數(shù)據(jù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!