我們都知道Feign其實(shí)也是通過HTTP請求來實(shí)現(xiàn)的通信 那么自然繞不開HTTP相關(guān)的東西,比如很多系統(tǒng)中權(quán)限校驗(yàn)都是通過Header中的參數(shù)來實(shí)現(xiàn),需要將前端傳過來的header轉(zhuǎn)發(fā)到目標(biāo)服務(wù),這里主要記錄一下關(guān)于Header的設(shè)置。
下面提到的同步/異步 只是記錄一下遇到問題的情景, 異步方法的實(shí)現(xiàn)方式同樣適用于同步方法
同步
同步方法一: 通過攔截器
這種方法是比較通用而且也比較常見的,通過實(shí)現(xiàn)Feign的RequestInterceptor接口,重寫里面的apply方法,為RequestTemplate添加請求頭信息
代碼如下
@Component
public class FeignClientsConfigurationCustom implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
// 此種方式是線程安全的
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
// 不為空時(shí)取出請求中的header 原封不動的設(shè)置到feign請求中
if (null != attributes) {
HttpServletRequest request = attributes.getRequest();
if (null != request) {
// 遍歷設(shè)置 也可從request取出整個(gè)Header 寫到RequestTemplate 中
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
requestTemplate.header(name, values);
}
}
}
}
}
}
完成上述代碼之后 在FeignClient注解中加入 configuration = FeignClientsConfigurationCustom.class 即可 如
@FeignClient(name = "testClient", configuration = FeignClientsConfigurationCustom.class)
public interface testServer{
}
同步方法二:@Headers注解
這種方法比較適用于一些不變的參數(shù),如Content-Type等
@FeignClient(name = "testClient", configuration = FeignClientsConfigurationCustom.class)
public interface testServer{
@GetMapping("/test")
@Headers({"Content-Type: application/json","Accept: application/json"})
String test(@RequestParam String param);
}
異步
此處說的異步場景是通過Spring中的
@Async實(shí)現(xiàn)的
如
// 帶有@Async注解,異步調(diào)用Feign
@Autowired
private TestService testService;
@GetMapping("/test")
public String test(){
// 此方法是一個(gè)異步方法, 在該方法中調(diào)用了Feign服務(wù)
testService.testMethod();
return "Hello world!";
}
此時(shí)的業(yè)務(wù)場景是 前端向我發(fā)送一個(gè)服務(wù),我異步地調(diào)用其他微服務(wù)的方法, 由于此方法執(zhí)行耗時(shí)可能會比較長,而且對用戶來說沒有下一步操作,所以直接return掉,那么主線程因return而關(guān)閉,此時(shí)在剛剛的FeignConfig中就無法獲取到請求頭了。
如圖所示,紅色方框圈起來的地方都不為null,但是最終獲取到的Header是一個(gè)空的Map。
所以猜測是因?yàn)橹骶€程退出觸發(fā)了JVM的回收機(jī)制。
那么此時(shí)的情況將是 主線程已經(jīng)退出,子線程沒有執(zhí)行完
所以顯然,此時(shí)不能通過這種方式傳遞Header了。
異步方法一:線程私有變量ThreadLocal。
既然無法直接通過獲取HttpServletRequest來獲取Header,那么可以稍微改造一下,在原來的基礎(chǔ)上添加一個(gè)攔截器。 所有的請求過來的時(shí)候,在攔截器中將Header先取出來,然后設(shè)置到本線程私有的Map中。
原來的apply方法在提交請求的時(shí)候再通過ThreadLocal提供的remove方法,清除掉。
只要把對該Map的操作封裝一個(gè)工具類,工具類中實(shí)現(xiàn)get/set方法即可。
其實(shí)這種方式就是換了一個(gè)地方保存請求頭,因此實(shí)用性與便捷性都還可以。
代碼與截圖待補(bǔ)充
異步方法二:通過傳參
該方式是在方法執(zhí)行前,先將需要的參數(shù)取出來,比如我需要一個(gè)token 就在Header中取出token,需要一個(gè)Content-Type就取出Content-Type。
然后將取出來的值作為參數(shù)傳遞到待執(zhí)行的方法中。
該方法所調(diào)用的Feign接口需要做一個(gè)改造,在參數(shù)中添加帶有@RequestHeader的注解,該注解表示將變量放在請求頭,而不是請求的參數(shù)或者請求體里面。
如
@FeignClient(name = "testClient", configuration = FeignClientsConfigurationCustom.class)
public interface testServer{
@GetMapping("/test")
String test(@RequestParam String param,@RequestHeader String token);
}
代碼與截圖待補(bǔ)充
- 本文作者: Patrick
- 本文鏈接: https://www.write1bug.cn/archives/feign請求頭設(shè)置問題同步異步
- 版權(quán)聲明: 本博客所有文章除特別聲明外,均采用CC BY-NC-SA 3.0 許可協(xié)議。轉(zhuǎn)載請注明出處!
