feign入門教程
1.介紹
feign簡單來說是一個java http客戶端,用來減少http API調用的複雜性。spring-Cloud-Netflix中就集成了feign客戶端用來訪問遠程的http服務,不管是用來作爲遠程調用客戶端,還是api接口測試都是非常方便的。
這裏要講的主要是OpenFeign。首先來看一下簡單的操作例子
2.示例
interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}
static class Contributor {
String login;
int contributions;
}
public static void main(String... args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
// Fetch and print a list of the contributors to this library.
List<Contributor> contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
@RequestLine定義了請求的http方法爲GET,uri爲/repos/{owner}/{repo}/contributors,{}中的參數會被@Param註解的參數所替換請求的url爲https://api.github.com。feign訪問此接口後會將返回的值轉換成List類型。
3.客戶端構建
feign客戶端使用builder模式構建,可以設置logLevel、contract、client、logger、encoder、decoder等配置。其中比較重要的
- encoder decoder 數據編解碼器
- client 配置發送http的客戶端。
- logLevel 配置日誌等級爲不輸出日誌、基礎日誌、輸出http頭、全部日誌
3.1 encoder decoder
feign默認使用第三方工具實現了很多編解碼器,如gson、jackson、Sax、JAXB
* 使用gson進行json序列化、反序化
GsonCodec codec = new GsonCodec();
GitHub github = Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
- 使用Jackson進行json序列化、反序化
GitHub github = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.target(GitHub.class, "https://api.github.com");
- 使用SAX進行xml反序化
api = Feign.builder()
.decoder(SAXDecoder.builder()
.registerContentHandler(UserIdHandler.class)
.build())
.target(Api.class, "https://apihost");
- 使用JAXB訪問xml api
api = Feign.builder()
.encoder(new JAXBEncoder())
.decoder(new JAXBDecoder())
.target(Api.class, "https://apihost");
3.2 client
- 使用OkHttp 客戶端進行http訪問
GitHub github = Feign.builder()
.client(new OkHttpClient())
.target(GitHub.class, "https://api.github.com");
- 使用Ribbon 客戶端進行http訪問,可以使用ribbon的路由功能
MyService api = Feign.builder().client(RibbonClient.create()).target(MyService.class, "https://myAppProd");
- 使用熔斷器Hystrix客戶端進行http訪問
MyService api = HystrixFeign.builder().target(MyService.class, "https://myAppProd");
其他一些第三方實現就不多說了,自己可以去github查找
4.feign註解
- @Param 需要發送的http參數,可以替換其他註解上的參數。如@RequestLine中uri參數 @Body中的請求體
- @Headers 定義http請求的請求頭 如定義content type @Headers(“Content-Type: application/x-www-form-urlencoded”)
- @RequestLine 定義http的請求行,定義了訪問和uri
- @Body 定義請體的模板,使用@Param參數替換
- @QueryMap 使用map作爲請求的參數鍵值對
interface LoginClient {
@RequestLine("POST /")
@Headers("Content-Type: application/xml")
@Body("<login \"user_name\"=\"{user_name}\" \"password\"=\"{password}\"/>")
void xml(@Param("user_name") String user, @Param("password") String password);
@RequestLine("POST /")
@Headers("Content-Type: application/json")
// json curly braces must be escaped!
@Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
void json(@Param("user_name") String user, @Param("password") String password);
}
...
client.xml("denominator", "secret"); // <login "user_name"="denominator" "password"="secret"/>
client.json("denominator", "secret"); // {"user_name": "denominator", "password": "secret"}
5.其他特性
5.1 接口繼承
對於一些通用的接口,uri相同,只有域名不同,可以使用帶泛型的接口的繼承特性,
如
interface BaseAPI {
@RequestLine("GET /health")
String health();
@RequestLine("GET /all")
List<Entity> all();
}
interface CustomAPI extends BaseAPI {
@RequestLine("GET /custom")
String custom();
}
@Headers("Accept: application/json")
interface BaseApi<V> {
@RequestLine("GET /api/{key}")
V get(@Param("key") String key);
@RequestLine("GET /api")
List<V> list();
@Headers("Content-Type: application/json")
@RequestLine("PUT /api/{key}")
void put(@Param("key") String key, V value);
}
interface FooApi extends BaseApi<Foo> { }
interface BarApi extends BaseApi<Bar> { }
5.2請求攔截器
實現RequestInterceptor接口在feign build的時候可以設置此攔截器,但是貌似並沒有攔截功能,只能添加一些共用的代碼
想要時間攔截可能需要拋出異常,中斷請求。
如添加一些公用頭部
static class ForwardedForInterceptor implements RequestInterceptor {
@Override public void apply(RequestTemplate template) {
template.header("X-Forwarded-For", "origin.host.com");
}
}
...
Bank bank = Feign.builder()
.decoder(accountDecoder)
.requestInterceptor(new ForwardedForInterceptor())
.target(Bank.class, "https://api.examplebank.com");
5.3 參數toString
@Param註解的參數默認使用ToStringExpander轉化爲String
可以在@Param中定義expander,實現public String expand(Object value) 方法。
@RequestLine("GET /?since={date}") Result list(@Param(value = "date", expander = DateToMillis.class) Date date);