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);