配置 Spring Cloud FeignClient URL
一、簡介
在本文中,我們將了解如何將目標 URL 提供給 Feign Client 界面。
2.概述
為了快速開始,我們將使用來自JSONPlaceholder網站的Albums, Posts,
和Todos
對象的模擬響應。
讓我們看看Album
類:
public class Album {
private Integer id;
private Integer userId;
private String title;
// standard getters and setters
}
和Post
類:
public class Post {
private Integer id;
private Integer userId;
private String title;
private String body;
// standard getters and setters
}
最後, Todo
類:
public class Todo {
private Integer id;
private Integer userId;
private String title;
private Boolean completed;
// standard getters and setters
}
3.在註解中添加基本URL
我們可以在客戶端界面@FeignClient
註解的url
屬性中設置base URL 。然後,我們將使用相關的 HTTP 動詞註釋方法並添加所需的端點:
@FeignClient(name = "albumClient", url = "https://jsonplaceholder.typicode.com/albums/")
public interface AlbumClient {
@GetMapping(value = "/{id}")
Album getAlbumById(@PathVariable(value = "id") Integer id);
}
讓我們添加一個 REST 控制器來測試我們的客戶端:
@RestController
public class ConfigureFeignUrlController {
private final AlbumClient albumClient;
// standard constructor
@GetMapping(value = "albums/{id}")
public Album getAlbumById(@PathVariable(value = "id") Integer id) {
return albumClient.getAlbumById(id);
}
// other controller methods
}
當目標 URL 在整個應用程序的生命週期中都是靜態的時,此選項很有用。
4. 使用配置屬性
或者,對於 Spring Cloud 2022.0.1 或更高版本,我們可以使用application.properties
將 URL 提供給 Feign Client 接口。屬性spring.cloud.openfeign.client.config.<interface-name>.url
用於此。在這裡, <interface-name>
是我們在@FeignClient
註解中提供的name
屬性的值:
@FeignClient(name = "postClient")
public interface PostClient {
@GetMapping(value = "/{id}")
Post getPostById(@PathVariable(value = "id") Integer id);
}
我們將在 application.properties 中添加基本 URL:
spring.cloud.openfeign.client.config.postClient.url=https://jsonplaceholder.typicode.com/posts/
對於低於 2022.0.1 的 Spring Cloud 版本,我們可以將url
屬性設置為@FeignClient
以從application.properties
中讀取值:
@FeignClient(name = "postClient", url = "${spring.cloud.openfeign.client.config.postClient.url}")
接下來,讓我們將這個客戶端注入到我們之前創建的控制器中:
@RestController
public class FeignClientController {
private final PostClient postClient;
// other attributes and standard constructor
@GetMapping(value = "posts/{id}")
public Post getPostById(@PathVariable(value = "id") Integer id) {
return postClient.getPostById(id);
}
// other controller methods
}
如果目標 URL 根據應用程序的環境而變化,則此選項很有用。例如,我們可能將模擬服務器用於開發環境,將實際服務器用於生產環境。
5.使用RequestLine
Spring Cloud 提供了一個特性,我們可以在其中覆蓋目標 URL 或在運行時直接提供 URL。這是通過使用@RequestLine
註釋並使用 Feign Builder API 手動創建 Feign 客戶端來註冊客戶端來實現的:
@FeignClient(name = "todoClient")
public interface TodoClient {
@RequestLine(value = "GET")
Todo getTodoById(URI uri);
}
我們需要在我們的控制器中手動創建這個假客戶端:
@RestController
@Import(FeignClientsConfiguration.class)
public class FeignClientController {
private final TodoClient todoClient;
// other variables
public FeignClientController(Decoder decoder, Encoder encoder) {
this.todoClient = Feign.builder().encoder(encoder).decoder(decoder).target(Target.EmptyTarget.create(TodoClient.class));
// other initialisation
}
@GetMapping(value = "todo/{id}")
public Todo getTodoById(@PathVariable(value = "id") Integer id) {
return todoClient.getTodoById(URI.create("https://jsonplaceholder.typicode.com/todos/" + id));
}
// other controller methods
}
這裡,我們首先通過FeignClientsConfiguration.class.
Feign.Builder
用於為 API 接口自定義這些屬性。我們可以配置encoder
、 decoder
、 connectTimeout
、 readTimeout
、 authentication 等屬性。
target
屬性定義了這些屬性將應用於哪個接口。這個接口有兩個實現:我們在這裡使用的EmptyTarget,
和HardCodedTarget
。 EmptyTarget
類在編譯時不需要 URL,而HardCodedTarget
需要。
值得注意的是,作為參數提供的 URI 參數將覆蓋@feignClient
註釋中提供的 URL 和配置屬性中的 URL。同樣, @FeignClient
註釋中提供的 URL 將覆蓋配置屬性中提供的 URL。
6. 使用RequestInterceptor
或者,另一種在運行時提供目標 URL 的方法是向 Feign.Builder 提供自定義RequestInterceptor
Feign.Builder.
在這裡,我們將覆蓋RestTemplate
的target
屬性,以將 URL 更新為通過requestInterceptor
提供給Feign.Builder
URL:
public class DynamicUrlInterceptor implements RequestInterceptor {
private final Supplier<String> urlSupplier;
// standard constructor
@Override
public void apply(RequestTemplate template) {
String url = urlSupplier.get();
if (url != null) {
template.target(url);
}
}
}
讓我們向AlbumClient.java:
@GetMapping(value = "/{id}")
Album getAlbumByIdAndDynamicUrl(@PathVariable(name = "id") Integer id);
我們不會在構造函數中使用 Builder,而是在ConfigureFeignUrlController
的方法中使用它來創建AlbumClient:
@RestController
@Import(FeignClientsConfiguration.class)
public class ConfigureFeignUrlController {
private final ObjectFactory<HttpMessageConverters> messageConverters;
private final ObjectProvider<HttpMessageConverterCustomizer> customizers;
// other variables, standard constructor and other APIs
@GetMapping(value = "/dynamicAlbums/{id}")
public Album getAlbumByIdAndDynamicUrl(@PathVariable(value = "id") Integer id) {
AlbumClient client = Feign.builder()
.requestInterceptor(new DynamicUrlInterceptor(() -> "https://jsonplaceholder.typicode.com/albums/"))
.contract(new SpringMvcContract())
.encoder(new SpringEncoder(messageConverters))
.decoder(new SpringDecoder(messageConverters, customizers))
.target(Target.EmptyTarget.create(AlbumClient.class));
return client.getAlbumByIdAndDynamicUrl(id);
}
}
在這裡,我們添加了上面創建的DynamicUrlInterceptor
,它接受一個 URL 來覆蓋AlbumClient
的默認 URL。我們還將客戶端配置為使用SpringMvcContract, SpringEncoder
和SpringDecoder.
當我們需要在我們的應用程序中提供對 webhook 的支持時,最後兩個選項將很有用,因為目標 URL 會因每個客戶端而異。
七、結論
在本文中,我們了解瞭如何以不同方式為 Feign Client 接口配置目標 URL。一如既往,完整的實現可以在 GitHub 上獲得。