V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
bryan31
V2EX  ›  Java

一款直击痛点的优秀 http 框架,让我超高效率完成了和第三方接口的对接

  •  
  •   bryan31 · 2020-08-25 11:10:23 +08:00 · 3021 次点击
    这是一个创建于 1551 天前的主题,其中的信息可能已经有所发展或是发生改变。

    首先,我是此文原作者,可能有同学看过这篇文章。在 V 站只是为了介绍 Forest 这个神器,帮助大家在工作中选择 http 客户端时多一个选择。望大家积极讨论。

    1.背景

    因为业务关系,要和许多不同第三方公司进行对接。这些服务商都提供基于 http 的 api 。但是每家公司提供 api 具体细节差别很大。有的基于RESTFUL规范,有的基于传统的 http 规范;有的需要在header里放置签名,有的需要SSL的双向认证,有的只需要SSL的单向认证;有的以JSON 方式进行序列化,有的以XML方式进行序列化。类似于这样细节的差别太多了。

    不同的公司 API 规范不一样,这很正常。但是对于我来说,我如果想要代码变得优雅。我就必须解决一个痛点:

    不同服务商 API 那么多的差异点,如何才能维护一套不涉及业务的公共 http 调用套件。最好通过配置或者简单的参数就能区分开来。进行方便的调用?

    我当然知道有很多优秀的大名鼎鼎的 http 开源框架可以实现任何形式的 http 调用,在多年的开发经验中我都有使用过。比如apachehttpClient包,非常优秀的Okhttpjersey client

    这些http开源框架的接口使用相对来说,都不太一样。不管选哪个,在我这个场景里来说,我都不希望在调用每个第三方的 http api 时写上一堆 http 调用代码。

    所以,在这个场景里,我得对每种不同的 http api 进行封装。这样的代码才能更加优雅,业务代码和 http 调用逻辑耦合度更低。

    可惜,我比较懒。一来觉得封装起来比较费时间,二来觉对封装这种底层 http 调用来说,应该有更好的选择。不想自己再去造轮子。

    于是,我发现了一款优秀的开源 http 框架,能屏蔽不同细节 http api 所带来的所有差异。能通过简单的配置像调用 rpc 框架一样的去完成极为复杂的 http 调用。

    Forest

    https://gitee.com/dt_flys/forest

    1

    2.上手

    Forest支持了Springboot的自动装配,所以只需要引入一个依赖就行

    <dependency>
      <groupId>com.dtflys.forest</groupId>
      <artifactId>spring-boot-starter-forest</artifactId>
      <version>1.4.0</version>
    </dependency>
    

    定义自己的接口类

    public interface MyClient {
    
        @Request(url = "http://baidu.com")
        String simpleRequest();
    
        @Request(
                url = "http://ditu.amap.com/service/regeo",
                dataType = "json"
        )
        Map getLocation(@DataParam("longitude") String longitude, @DataParam("latitude") String latitude);
      
    }
    

    在启动类里配置代理接口类的扫描包

    @SpringBootApplication
    @ForestScan(basePackages = "com.example.demo.forest")
    public class DemoApplication {
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    }
    

    这时候,你就可以从 spring 容器中注入你的代理接口,像调用本地方法一样去调用 http 的 api 了

    @Autowired
    private MyClient myClient;
    
    @Override
    public void yourMethod throws Exception {
        Map result = myClient.getLocation("124.730329","31.463683");
        System.out.println(JSON.toJSONString(result,true));
    }
    

    日志打印,Forest打印了内部所用的 http 框架,和实际请求 url 和返回。当然日志可以通过配置去控制开关。

    1

    3.特点

    我觉得对于尤其是做对接第三方 api 的开发同学来说,这款开源框架能帮你提高很多效率。

    Forest 底层封装了 2 种不同的 http 框架:Apache httpClientOKhttp。所以这个开源框架并没有对底层实现进行重复造轮子,而是在易用性上面下足了功夫。

    我用Forest最终完成了和多个服务商 api 对接的项目,这些风格迥异的 API,我仅用了 1 个小时时间就把他们转化为了本地方法。然后项目顺利上线。

    Forest作为一款更加高层的 http 框架,其实你并不需要写很多代码,大多数时候,你仅通过一些配置就能完成 http 的本地化调用。而这个框架所能覆盖的面,却非常之广,满足你绝大多数的 http 调用请求。

    Forest有以下特点:

    • HttpclientOkHttp为后端框架
    • 通过调用本地方法的方式去发送 Http 请求, 实现了业务逻辑与 Http 协议之间的解耦
    • 相比 Feign 更轻量,不依赖Spring Cloud和任何注册中心
    • 支持所有请求方法:GET, HEAD, OPTIONS, TRACE, POST, DELETE, PUT, PATCH
    • 支持灵活的模板表达式
    • 支持过滤器来过滤传入的数据
    • 基于注解、配置化的方式定义Http请求
    • 支持SpringSpringboot集成
    • 实现JSONXML的序列化和反序列化
    • 支持 JSON 转换框架: Fastjson,Jackson, Gson
    • 支持JAXB形式的XML转换
    • 支持SSL的单向和双向加密
    • 支持 http 连接池的设定
    • 可以通过OnSuccessOnError接口参数实现请求结果的回调
    • 配置简单,一般只需要@Request一个注解就能完成绝大多数请求的定义
    • 支持异步请求调用

    4.两个很棒的功能

    这里不对使用方式和配置方式一一描述,有兴趣的可以去阅读详细文档:

    https://dt_flys.gitee.io/forest

    这里只想分析这个框架 2 个我认为比较好的功能

    4.1 模板表达式和参数的映射绑定功能


    模板表达式在使用的时候特别方便,举个栗子

    @Request(
        url = "${0}/send?un=${1}&pw=${2}&ph=${3}&ct=${4}",
        type = "get",
        dataType = "json"
    )
    public Map send(
        String base,
        String userName,
        String password,
        String phone,
        String content
    );
    

    上述是用序号下标进行取值,也可以通过名字进行取值:

    
    @Request(
        url = "${base}/send?un=${un}&pw=${pw}&ph=${3}&ct=${ct}",
        type = "get",
        dataType = "json"
    )
    public Map send(
        @DataVariable("base") String base,
        @DataVariable("un") String userName,
        @DataVariable("pw") String password,
        @DataVariable("ph") String phone,
        @DataVariable("ct") String content
    );
    

    甚至于可以这样简化写:

    @Request(
        url = "${base}/send",
        type = "get",
        dataType = "json"
    )
    public Map send(
        @DataVariable("base") String base,
        @DataParam("un") String userName,
        @DataParam("pw") String password,
        @DataParam("ph") String phone,
        @DataParam("ct") String content
    );
    

    以上三种写法是等价的

    当然你也可以把参数绑定到 header 和 body 里去,你甚至于可以用一些表达式简单的把对象序列化成 json 或者 xml:

    @Request(
        url = "${base}/pay",
      	contentType = "application/json",
        type = "post",
        dataType = "json",
        headers = {"Authorization: ${1}"},
        data = "${json($0)}"
    )
    public PayResponse pay(PayRequest request, String auth);
    

    当然数据绑定这块详情请参阅文档

    4.2 对HTTPS的支持

    以前用其他 http 框架处理 https 的时候,总觉得特别麻烦,尤其是双向证书。每次碰到问题也只能去 baidu 。然后根据别人的经验来修改自己的代码。

    Forest对于这方面也想的很周到,底层完美封装了对 https 单双向证书的支持。也是只要通过简单的配置就能迅速完成。举个双向证书栗子:

    @Request(
        url = "${base}/pay",
      	contentType = "application/json",
        type = "post",
        dataType = "json",
      	keyStore = "pay-keystore",
      	data = "${json($0)}"
    )
    public PayResponse pay(PayRequest request);
    

    其中pay-keystore对应着application.yml里的ssl-key-stores

    forest:
      ...
      ssl-key-stores:
        - id: pay-keystore
          file: test.keystore
          keystore-pass: 123456
          cert-pass: 123456
          protocols: SSLv3
    

    这样设置,就 ok 了,剩下的,就是本地代码形式的调用了。

    5.最后

    Forest有很多其他的功能设定,如果感兴趣的同学还请仔细去阅读文档和示例。

    但是我想说的是,相信看到这里,很多人一定会说,这不就是Feign吗?

    我在开发Spring Cloud项目的时候,也用过一段时间Feign,个人感觉Forest的确在配置和用法上和Feign的设计很像,但Feign的角色更多是作为Spring Cloud生态里的一个成员。充当 RPC 通信的角色,其承担的不仅是 http 通讯,还要对注册中心下发的调用地址进行负载均衡。

    Forest这个开源项目其定位则是一个高阶的 http 工具,主打友好和易用性。从使用角度出发,个人感觉Forest配置性更加简单直接。提供的很多功能也能解决很多人的痛点。

    开源精神难能可贵,好的开源需要大家的添砖加瓦和支持。希望这篇文章能给大家在选择 http 客户端框架时带来一个新的选择:Forest

    6.联系作者

    觉得有用的话,请关注下我的公众号「元人部落」,作者坚持原创的内容技术分享,也有开源作品,欢迎关注

    开源仓库为: https://gitee.com/bryan31

    公众号一般周更,每次会分享一些实用的技术,陪你一起成长

    关注后回复“资料”领取 50G 的视频资料,包括一套企业级微服务的视频教学

    offIical-wx

    9 条回复    2020-08-25 16:04:04 +08:00
    gz911122
        1
    gz911122  
       2020-08-25 11:26:30 +08:00
    你这个注解,感觉不如 retrofit 啊
    丑了很多
    qmzhixu
        2
    qmzhixu  
       2020-08-25 11:35:51 +08:00
    感觉封装度太高了,还是 okhttp 简单,调试也方便
    BBCCBB
        3
    BBCCBB  
       2020-08-25 11:45:12 +08:00
    我感觉和 feign 一样的...
    sagaxu
        4
    sagaxu  
       2020-08-25 11:50:32 +08:00 via Android
    1. 支持异步吗?连接池呢?
    2. 能不能脱离 spring 单独使用?
    bryan31
        5
    bryan31  
    OP
       2020-08-25 11:53:13 +08:00
    @sagaxu 都可以的
    zzl22100048
        6
    zzl22100048  
       2020-08-25 12:11:40 +08:00 via iPhone
    这类的有很多,Resteasy client,feign,Microprofile RestClient,都是用接口代理请求
    jones2000
        7
    jones2000  
       2020-08-25 13:40:34 +08:00
    没什么特色,只是封装一下, 都是重复劳动。
    你既然是封装了第 3 方 api, 那是不是可以对多个第 3 放 api 进行数据集的筛选过滤操作呢。
    如果 接口 1 是国内每个省的 GDP 数据, 接口 2 是取国内每个省的人口出生率。 你的插件是否可以自动生成 GDP>3% 人口出生率>4W 的数据 api 。
    这样才能减少 api 的开发, 通过自动化生成 api 数据。
    gz911122
        8
    gz911122  
       2020-08-25 13:42:56 +08:00
    @bryan31 可以写下和 retrofit 的优缺点对比.
    毕竟 retrofit 是 java 领域这方面 star 最高的开源项目, 不过不能做到某些方面更优秀, 那么没什么意义
    vate32
        9
    vate32  
       2020-08-25 16:04:04 +08:00
    值得关注一波
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   954 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 22:47 · PVG 06:47 · LAX 14:47 · JFK 17:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.