Feign

简介

Feign 是一个 Java 到 HTTP 客户端的粘合剂。Feign 的目标是以最少的开销和代码把 你的代码连接到 http api 上。通过定制的编解码器和错误处理,你可以请求任何基于文本的 http api 。

原理

通过处理注解信息来生成模板化请求。在发出请求前,参数直接应用到这些模板上。这限制了 Feign 只支持基于文本的 api,这显著简化了系统的一些方面如重放请求。

为什么选择 Feign

  • 依赖问题。目前项目组用的是 Hessian 做远程调用,由于 Hessian 存在对 jar 包的依赖,特别是一些项目升级到 JDK 1.8,采用 Maven 构建;而老的一些任然采用 1.6 ,是个简单的 Eclipse 工程,导致打包、部署非常麻烦。

  • 依赖于接口,使用简洁。对于客户端,只依赖于接口类,通过 Spring 注入实现,迁移基本不需要很大的改动。

  • 与 Spring Cloud 集成。Feign 本身是 Spring Cloud 的一个组件,可以通过 Ribbon 做路由,可以进一步提升服务化。

  • 系统对性能的要求并不是那种很严苛的,基于文本的调用也方便调试。

使用举例

首先要定义 Java 代码到 HTTP 请求模板的注解,如下,在接口的方法上进行。

public interface HttpApi {
    // 提交一些简单类型的参数,返回一个复合对象的列表
    @RequestLine("GET /repos/{owner}/{repo}/contributors")
    List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);

    // 指定 http 头的一些字段。提交复合类型的参数
    @RequestLine("PUT /repos/contributors/{id}")
    @Headers("Content-Type: application/json")
    void putContributor(@Param("id") String id, Contributor contributor);

    // 定制请求体的内容
    @RequestLine("POST /login")
    @Headers("Content-Type: application/json")
    // json 的花括号必须进行转义
    @Body("%7B\"name\": \"{name}\", \"password\": \"{password}\"%7D")
    void login(@Param("name") String name, @Param("password") String password);
}

调用;

HttpApi httpApi = Feign.builder().decoder(new GsonDecoder()).encoder(new GsonEncoder()).target(HttpApi.class,
                "https://api.github.com");
List<Contributor> contributors = httpApi.contributors("OpenFeign", "feign");

如果参数或返回值有复合对象,Feign 内置的编解码器是不支持的,可以使用基于 Gson 实现的 feign-gson 库。

构建器的 target 方法接收一个接口类和目标 http api 的基路径(跟 @RequestLine 里指定的路径组合起来就是完整的请求路径)。

使用方请求 http api 时就像调用本地方法一样:httpApi.contributors("OpenFeign", "feign");

对于没有在 注解里指定的参数,即使用了 @Param 注解,都放进一个 Map 里,编码后作为请求体。
这会导致服务器端没法把参数映射到多个 POJO 上。

@RequestLine("PUT /repos/contributors/params/{id}")
@Headers("Content-Type: application/json")
void putContributorParam(@Param("id") String id, @Param("contributor1") String contributor1,
        @Param("contributor2") String contributor2);

以上面的方式提交的 Feign 会把 contributor1, contributor2 参数放进一个 Map 里,然后把这个 Map 序列化为 JSON 串作为请求体发送出去。这也是默认的方式。

如果需要以 方式发送请求,需要手工对参数值进行 URLEncode 。

@RequestLine("PUT /repos/contributors/params/{id}")
@Headers("Content-Type: application/x-www-form-urlencoded")
@Body("contributor1={contributor1}&contributor2={contributor2}")
void putContributorParam(@Param("id") String id, @Param("contributor1") String contributor1,
        @Param("contributor2") String contributor2);

调用: httpApi.putContributorParam("123654", "+-*/=中文&contributor2=123", "0123457896");

Content-Type: application/x-www-form-urlencoded
Content-Length: 42

contributor1=+-*/=&contributor2=0123457896

服务端得到的参数的 contributor1 是 " -*/=中文",contributor2 是 "123,0123457896"


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据