在开发 APP 端 API 接口时,随着 APP 的版本迭代,尽管通常 APP 只需要保持 4-5 个版本可用,过老版本会强制更新,但 API 接口避免不了出现多个版本的情况,那么 API 接口的多版本问题服务端怎么解决呢?
要实现 API 的版本控制,常见的方法就是引入版本号。本文结合 Yii 2 来进行演示。
版本号
传递 API 的版本号大致有两种方式:
- 版本号配置为子域名,但由于版本号变更频繁,该方式采用较少;
- 版本号嵌入 URL 中,如百度 API 的
http://api.map.baidu.com/direction/v2/transit
; - 版本号放入 HTTP 请求头的 Accept 中,如
Accept: application/json; version=1
;
这两种方式都存在不足,第 1 种版本号跟资源不相关,所以违背 Restful 风格,第 2 种接口版本信息又不够直观。Yii 2 中混合了这两种方法实现了主版本号和小版本号,如下:
- 把每个主版本的 API 实现在一个单独的模块(例如 v1,v2),因此,API 的 URL 会包含主版本号;
- 在每一个主要版本(即相应的模块),使用 Accept 请求头确定小版本号实现具体业务逻辑;
之所以使用大小版本号是为了更好地分离代码。当小迭代或者 bug 修复时,更新小版本号,大的需求变更或者一次开发周期中迭代次数较多则更新大版本号。
版本兼容
每个版本代码放置于一个独立的目录下,由于项目中每个 API 同时存在多个版本,如果都是独立的多份代码,相邻版本之间逻辑大致相同,所以代码冗余较高,另外存在需要修改多份代码的情况,不易维护。
另一种方式是通过调整代码结构,新版本 继承 上一个版本,通过 重写 来更好地进行功能迭代和升级,同时也能版本兼容。
目录结构
根据大版本号分离成模块后,项目目录结构如下:
api/ |
代码示例
在编码时,尽量将每个版本公有逻辑提出到 common 下,只将版本特有逻辑放置于对应版本下。
common
公有部分,包括公有逻辑,数据源 model 等,放置于 controllers、models 部分。
- BaseAction.php
BaseAction 用户处理小版本路由,后续的 Action 都继承自此。
namespace api\controllers; |
- RoomModel.php
namespace api\models; |
- RoomLogic.php
namespace api\models\logics; |
v1
v1 版本为初始版本,大部分逻辑只需继承自公有逻辑common/RoomLogic.php
。
- v1/RoomController.php
namespace api\modules\v1\controllers; |
- v1/ListAction.php
namespace api\modules\v1\controllers\room; |
- v1/RoomLogic.php
namespace api\modules\v1\models\logics; |
v1 版结果如下:
//请求信息 |
v2
v2 版逻辑对 v1 版进行了扩展,比如返回小区房源的最低价、小区房源总数等。
- v2/RoomController.php
namespace api\modules\v2\controllers; |
- v2/ListAction.php
namespace api\modules\v2\controllers\room; |
- v2/RoomLogic.php
namespace api\modules\v2\models\logics; |
v2 版结果如下:
//请求信息 |
v3
如果某一天,数据源需要从 solr 切换到 es,那么只需改写共有RoomLogic.php
并保持数据结构不变,老版本数据也就切换为 es 了。
RoomModel 修改:
namespace api\models; |
RoomLogic 部分修改 countRoomByResblock 方法:
namespace api\models\logics; |
v3 版结果如下:
v3: |
老版本 v2 版结果如下;
v2: |
总结
本文叙述的方式,虽然多个版本时代码不会冗余,但是每个版本之间会有较强的依赖关系,并没有做到应用解耦。实际中还需根据业务场景选择合适的版本处理方案,本文仅提供一种实现思路。