Feign請求頭設(shè)置/傳遞問題(同步方法設(shè)置Header/異步方法設(shè)置Header)

我們都知道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ǔ)充

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容