### 2018 年 11 月 3 日 发布 我们假设API接口的域名名为`api.tp5.com`,并且以两个版本`v1`和`v2`为例(注意,版本号仅为主版本,小版本应该是直接升级,不应该存在共存情况,所以`v1.1`或者`v2.0`这种版本号不应该设计在URL里面),来说明下API版本的不同控制方式,以及应该如何进行开发的规划。 ## 通过子域名(或子目录) 第一个办法,是直接使用两个模块(或者应用)来实现,对于架构改变比较大的API版本(尤其是不同版本之间基本没法共用、更改框架甚至采用不同的语言实现)通常会这样选择。 目录结构如下: ``` api ├─application │ ├─v1 │ │ ├─controller │ │ ├─model │ │ ├─config │ │ └─ ... │ ├─v2 │ │ ├─controller │ │ ├─model │ │ ├─config │ │ └─ ... │ ... ``` 请求方式 ``` GET https://api.tp5.com/v1/user/1 GET https://api.tp5.com/v2/user/1 ``` 当然,你也可以通过子域名绑定模块实现下面的方式访问 ``` GET https://v1.api.tp5.com/user/1 GET https://v2.api.tp5.com/user/1 ``` ## 通过请求参数 对于刚开始没有做好版本规划,后期迭代维护过程中增加了新的版本,考虑到架构的改造成本,可能会考虑下面的方式: ``` GET https://api.tp5.com/user/1 GET https://api.tp5.com/user/1?version=v2 ``` 由于缺乏很好的路径和类库目录规范,如果频繁更新版本的话,建议把版本的架构设计升级成后面的两种方式。 ## 通过路由 可能大多数接口在设计的时候已经考虑到了版本控制的问题,那么通常会选择在URL地址中增加版本标识参数,这种方式便于调试。 对于API应用来说,更建议采用**单模块设计+多级控制器**,目录结构如下: ``` api ├─application │ ├─controller │ │ ├─v1 │ │ ├─v2 │ │ └─ ... │ ├─model │ ... ``` 路由规则定义如下: ``` Route::get(':version/user/:id',':version.User/read'); ``` ``` GET https://api.tp5.com/v1/user/1 GET https://api.tp5.com/v2/user/1 ``` 由于使用了多级控制器,需要注意控制器的命名空间。 通过命令行可以快速的创建控制器文件: ``` php think make:controller v1/User ``` ## 通过头信息 最新的规范趋向于通过头信息来定义版本,优势在于从历史版本迭代更新的时候不需要改变URL地址,改变请求头信息即可,主要分为两种。 第一种是使用自定义请求头例如`api-version`控制版本(同理你还可以用其它头信息控制其它) ``` GET https://api.tp5.com/user/1 api-version:v2 ``` 头信息的方式,路由规则的定义略微做下调整即可: ``` use think\facade\Request; use think\facade\Route; $version = Request::header('api-version') ? : 'v1'; Route::get('user/:id', $version . '.User/read'); ``` 也有很多采用了`Accept`头信息来处理(好处是可以设置接口输出格式),通常的规范是 ``` GET https://api.tp5.com/user/1 Accept: application/vnd.tp5.v2+json ``` ## 总结 对于API接口开发,尽量事先做好版本控制规划,确保你的应用能兼容新老版本的访问。