1. 概述
有時,在使用 Feign 時,我們需要在 HTTP 調用中設置請求頭。Feign 允許我們使用聲明式語法構建 HTTP 客户端。
在本教程中,我們將學習如何使用註解配置請求頭。我們還將學習如何通過攔截器包含常用的請求頭。
2. 示例
在本文教程中,我們將使用一個示例,該示例暴露了 REST API 端點:書店應用程序。您可以輕鬆克隆項目並在本地運行它:
$ mvn install spring-boot:run讓我們深入探討客户端實現。
3. 使用 Header 註解
讓我們考慮一個場景,其中特定的 API 調用始終應包含靜態 Header。在這種情況下,我們可能會將該請求 Header 配置為客户端的一部分。一個典型的例子是包含 Content-Type Header。
使用 @Header 註解,我們可以輕鬆配置靜態請求 Header。 我們可以靜態地或動態地定義該 Header 值。
3.1. 設置靜態頭部值
讓我們配置兩個靜態頭部,即<em >Accept-Language</em >和<em >Content-Type</em >,到<em >BookClient</em >中:
@Headers("Accept-Language: en-US")
public interface BookClient {
@RequestLine("GET /{isbn}")
BookResource findByIsbn(@Param("isbn") String isbn);
@RequestLine("POST")
@Headers("Content-Type: application/json")
void create(Book book);
}在上述代碼中,Accept-Language 標頭包含在所有 API 中,因為它應用於 BookClient。然而,create 方法還包含額外的 Content-Type 標頭。
接下來,讓我們看看如何使用 Feign 的 Builder 方法創建 BookClient,並傳遞 HEADERS 日誌級別:
Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.logger(new Slf4jLogger(type))
.logLevel(Logger.Level.HEADERS)
.target(BookClient.class, "http://localhost:8081/api/books");
現在,讓我們測試 create 方法:
String isbn = UUID.randomUUID().toString();
Book book = new Book(isbn, "Me", "It's me!", null, null);
bookClient.create(book);
book = bookClient.findByIsbn(isbn).getBook();然後,讓我們驗證輸出日誌中的頭部:
18:01:15.039 [main] DEBUG c.b.f.c.h.staticheader.BookClient - [BookClient#create] Accept-Language: en-US
18:01:15.039 [main] DEBUG c.b.f.c.h.staticheader.BookClient - [BookClient#create] Content-Type: application/json
18:01:15.096 [main] DEBUG c.b.f.c.h.staticheader.BookClient - [BookClient#findByIsbn] Accept-Language: en-US請注意,如果客户端接口和API方法中標頭名稱相同,則它們不會相互覆蓋。相反,請求將包含所有這些值。
3.2. 設置動態請求頭值
使用 <em >@Header</em> 註解,我們還可以設置動態請求頭值。為此,我們需要將值表達為佔位符。
將 <em >x-requester-id</em> 請求頭包含到 <em >BookClient</em> 中,佔位符為 <em >requester</em>:
@Headers("x-requester-id: {requester}")
public interface BookClient {
@RequestLine("GET /{isbn}")
BookResource findByIsbn(@Param("requester") String requester, @Param("isbn") String isbn);
}我們創建了一個名為 x-requester-id 的變量,並將其傳遞到每個方法中。我們使用 @Param 註解來匹配變量名。它會在運行時擴展以滿足由 @Headers 註解指定的頭部。
現在,讓我們使用 BookClient API,並添加 x-requester-id 頭部:
String requester = "test";
book = bookClient.findByIsbn(requester, isbn).getBook();然後,讓我們在輸出日誌中驗證請求頭:
18:04:27.515 [main] DEBUG c.b.f.c.h.s.parameterized.BookClient - [BookClient#findByIsbn] x-requester-id: test4. 使用 HeaderMaps 註解
讓我們設想一個場景,其中 header 鍵和值都是動態的。在這種情況下,可能的鍵範圍事先是未知的。此外,在同一客户端的不同方法調用之間,header 也可能不同。一個典型的例子是設置某些元數據 header。
使用一個 Map 參數,並使用 @HeaderMap,可以設置動態 header:
@RequestLine("POST")
void create(@HeaderMap Map<String, Object> headers, Book book);現在,讓我們嘗試使用 header map 測試 create 方法:
Map<String,Object> headerMap = new HashMap<>();
headerMap.put("metadata-key1", "metadata-value1");
headerMap.put("metadata-key2", "metadata-value2");
bookClient.create(headerMap, book);然後,讓我們驗證輸出日誌中的標頭:
18:05:03.202 [main] DEBUG c.b.f.c.h.dynamicheader.BookClient - [BookClient#create] metadata-key1: metadata-value1
18:05:03.202 [main] DEBUG c.b.f.c.h.dynamicheader.BookClient - [BookClient#create] metadata-key2: metadata-value25. 請求攔截器
攔截器可以對每個請求或響應執行各種隱式任務,例如日誌記錄或身份驗證。
Feign 提供了一個 RequestInterceptor 接口。 通過此接口,我們可以添加請求頭。
當已知請求頭應包含在每個調用中時,添加一個請求攔截器是有意義的。 這種模式消除了調用代碼實施非功能性要求(如身份驗證或跟蹤)的依賴性。
讓我們通過實現一個 AuthorisationService 來嘗試一下,該服務將用於生成授權令牌:
public class ApiAuthorisationService implements AuthorisationService {
@Override
public String getAuthToken() {
return "Bearer " + UUID.randomUUID();
}
}現在,讓我們來實現我們的自定義請求攔截器:
public class AuthRequestInterceptor implements RequestInterceptor {
private AuthorisationService authTokenService;
public AuthRequestInterceptor(AuthorisationService authTokenService) {
this.authTokenService = authTokenService;
}
@Override
public void apply(RequestTemplate template) {
template.header("Authorisation", authTokenService.getAuthToken());
}
}
請注意,請求攔截器可以讀取、刪除或修改任何請求模板的任何部分。
現在,讓我們使用builder方法將AuthInterceptor添加到BookClient中:
Feign.builder()
.requestInterceptor(new AuthInterceptor(new ApiAuthorisationService()))
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.logger(new Slf4jLogger(type))
.logLevel(Logger.Level.HEADERS)
.target(BookClient.class, "http://localhost:8081/api/books");
然後,讓我們使用帶有 Authorisation 標頭的 BookClient API 進行測試:
bookClient.findByIsbn("0151072558").getBook();現在,讓我們驗證輸出日誌中的標題:
18:06:06.135 [main] DEBUG c.b.f.c.h.staticheader.BookClient - [BookClient#findByIsbn] Authorisation: Bearer 629e0af7-513d-4385-a5ef-cb9b341cedb5可同時應用於 Feign 客户端的多個請求攔截器。但對於它們應用的順序,並未提供任何保證。
6. 結論
在本文中,我們探討了 Feign 客户端如何支持設置請求頭。我們通過使用 <em @Headers</em>、<em @HeaderMaps</em>> 註解以及請求攔截器來實現這一功能。