feign简单来说是一个java http客户端,用来减少http API调用的复杂性。spring-Cloud-Netflix中就集成了feign客户端用来访问远程的http服务,不管是用来作为远程调用客户端,还是api接口测试都是非常方便的。
这里要讲的主要是OpenFeign。首先来看一下简单的操作例子
interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List 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 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类型。
feign客户端使用builder模式构建,可以设置logLevel、contract、client、logger、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");
GitHub github = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.target(GitHub.class, "https://api.github.com");
api = Feign.builder()
.decoder(SAXDecoder.builder()
.registerContentHandler(UserIdHandler.class)
.build())
.target(Api.class, "https://apihost");
api = Feign.builder()
.encoder(new JAXBEncoder())
.decoder(new JAXBDecoder())
.target(Api.class, "https://apihost");
GitHub github = Feign.builder()
.client(new OkHttpClient())
.target(GitHub.class, "https://api.github.com");
MyService api = Feign.builder().client(RibbonClient.create()).target(MyService.class, "https://myAppProd");
MyService api = HystrixFeign.builder().target(MyService.class, "https://myAppProd");
其他一些第三方实现就不多说了,自己可以去github查找
interface LoginClient {
@RequestLine("POST /")
@Headers("Content-Type: application/xml")
@Body(" ")
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"); // "user_name"="denominator" "password"="secret"/>
client.json("denominator", "secret"); // {"user_name": "denominator", "password": "secret"}
对于一些通用的接口,uri相同,只有域名不同,可以使用带泛型的接口的继承特性,
如
interface BaseAPI {
@RequestLine("GET /health")
String health();
@RequestLine("GET /all")
List 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 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> { }
实现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");
@Param注解的参数默认使用ToStringExpander转化为String
可以在@Param中定义expander,实现public String expand(Object value) 方法。
@RequestLine("GET /?since={date}") Result list(@Param(value = "date", expander = DateToMillis.class) Date date);