需求:javascript
为客户端同事写接口文档的各位后端同窗,已经在各类场合回忆了使用自动化文档工具前手写文档的血泪史.
个人故事却又不一样,由于首先来讲,我在公司是 Android 组负责人,属于上述血泪史中催死人不偿命的客户端阵营.
但血泪史倒是相通的,没有自动化文档的日子,对接口就是开发流程中最低效的环节.
所以决定使用 swagger 搭建由php注释生成文档的流程.php
背景:html
咱们的 restful api 项目采用 phalcon 框架,总体结构很简单,咱们只须要用 swagger 扫描 controller 目录便可.
下简称咱们的 php api 项目为 php_api_project.
服务器采用 nginx.前端
先说下最终的文档生成流程会是什么样子,以便先有个总体的认识:
搭建完成后, 整个流程, 从文档生成到前端展示, 大致以下:java
实现此需求只须要 swagger 的以下两个项目:
swagger-php: 扫描 php 注释的工具. 内含一个不错的例子.
swagger-ui: 用以将扫描工具生成的 swagger.json 文件内容展现在网页上.nginx
首先将这两个项目下载到本地:git
$ git clone https://github.com/swagger-api/swagger-ui.git $ git clone https://github.com/zircote/swagger-php.git
说是部署,主要就是产生 bin/swagger 这个用来生成 swagger.json 文件的命令.
主要工做,就是用 composer 解决下依赖就能够了.
由于国内直接用 composer 比较蛋疼,因此最好设置下国内的那个 composer 源.
这样的话, 整个 文档生成工具的部署 就是下面三行命令:github
$ cd swagger-php $ composer config repo.packagist composer https://packagist.phpcomposer.com $ composer update
只要中间不报错,就算部署完成了. 完成后能够生成一份文档试一下.
swagger-php 项目下的 Examples 目录下有一个示例php工程,里面已经用 swagger 格式写了各类接口注释, 咱们来尝试生成一份文档.
执行下面命令:apache
$ cd swagger-php $ mkdir json_docs $ php ./bin/swagger ./Examples -o json_docs/
上面命令会扫描 Examples 目录中的php文件注释, 而后在 json_docs 目录下生成 swagger.json 文件.
这个 swagger.json 文件就是前端 swagger-ui 用来展现的咱们的api文档文件.json
NOTE: swagger-php 只是个工具,放在哪里均可以.
部署方法很简单,就三步:
NOTE1: 只须要拷贝dist这一个文件夹就能够了.最好重命名下,简单起见,这里再也不重命名.
NOTE2: 咱们的项目根目录和 nginx 配置的 root 是同一个目录.其实不用放跟目录,只要放到一个不用跨域就跨域访问的目录就能够了. 为啥有跨域问题? 后面会讲.
只改一行就能够.
简单起见,这里直接将 swagger.json 目录指定在 dist 目录下便可. 咱们这里屡一下预设条件:
假设 php_api_project 项目的 host 是 api.my_project.com;
假设 php_api_project 项目在 nginx 中指定的 root 即为其根目录;
假设 swagger-ui 里的 dist 文件夹放在上述根目录中;
假设 swagger.json 文件就打算放在上述 dist 目录下 (php_api_project/dist/swagger.json) ;
那么 index.html 中把下面的片断改为这样:
var url = window.location.search.match(/url=([^&]+)/); if (url && url.length > 1) { url = decodeURIComponent(url[1]); } else { <!-- 就是这行,改为你生成的 swagger.json 能够被访问到的路径便可 --> url = "http://api.my_project.com/dist/swagger.json"; }
# 把 swagger-php_dir 这个,换成你的 swagger-php 录便可 cp swagger-php_dir/json_docs/swagger.json php_api_project/dist/
上述步骤完成后, 访问 http://api.my_project.com/dis... 就能够看到 Examples 那个小项目的 api 文档了.
swagger-php 项目的 Example 中已经有了不少相关例子,照着复制粘贴就能够了.
更具体的相关注释规则的文档,看这里:
http://bfanger.nl/swagger-exp...
假设个人项目 controller 所在目录为 php_api_project/controller/, 那么我只须要扫描这个目录就能够了,不用扫描整个 php 工程.
为了在 swagger.json 中生成某些统一的配置, 创建 php_api_project/controller/swagger 目录. 目录存放一个没有代码的php文件,里面只写注释.
我给这个文件取名叫 Swagger.php, 大致内容以下:
<?php /** * @SWG\Swagger( * schemes={"http"}, * host="api.my_project.com", * consumes={"multipart/form-data"}, * produces={"application/json"}, * @SWG\Info( * version="2.3", * title="my project doc", * description="my project 接口文档, V2-3.<br> 之后你们就在这里愉快的对接口把!<br> 之后你们就在这里愉快的对接口把!<br> 之后你们就在这里愉快的对接口把!<br> " * ), * * @SWG\Tag( * name="User", * description="用户操做", * ), * * @SWG\Tag( * name="MainPage", * description="首页模块", * ), * * @SWG\Tag( * name="News", * description="新闻资讯", * ), * * @SWG\Tag( * name="Misc", * description="其余接口", * ), * ) */
如上所示,个人这个php文件一行php代码也没有,就只有注释,为了定义一些全局的swagger设置:
schemes
: 使用协议 (能够填多种协议)host
: 项目地址, 这个地址会做为每一个接口的 url base ,拼接起来一期做为访问地址consumes
: 接口默认接收的MIME类型, 个人例子中的 formData 对应post表单类型. 注意这是项目默认值,在单个接口注释里能够复写这个值.produces
: 接口默认的回复MIME类型. api接口用的比较多的就是 application/json
和 application/xml
.@SWG\Info
: 这个里面填写的东西,会放在文档的最开头,用做文档说明.@SWG\Tag
: tag是用来给文档分类的,name字段必须惟一.某个接口能够指定多个tag,那它就会出如今多组分类中. tag也能够不用在这里预先定义就可使用,但那样就没有描述了. 多说无益,稍微用用就啥都明白了.
而后就是给每一个接口编写 swagger 格式的注释了.仍是举个栗子吧:
/** * @SWG\Post(path="/user/login", tags={"User"}, * summary="登陆接口(用户名+密码)", * description="用户登陆接口,帐号可为 用户名 或 手机号. 参考(这个会在页面产生一个可跳转的连接: [用户登陆注意事项](http://blog.csdn.net/liuxu0703/)", * @SWG\Parameter(name="userName", type="string", required=true, in="formData", * description="登陆用户名/手机号" * ), * @SWG\Parameter(name="password", type="string", required=true, in="formData", * description="登陆密码" * ), * @SWG\Parameter(name="image_list", type="string", required=true, in="formData", * @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/Image")), * description="用户相册. 好吧,没人会在登陆时要求填一堆图片信息.这里是为了示例 带结构的数据, @SWG\Schema ,这个结构须要另行定义,下面会讲." * ), * @SWG\Parameter(name="video", type="string", required=true, in="formData", * @SWG\Schema(ref="#/definitions/Video"), * description="用户 呃... 视频? 同上,为了示例 @SWG\Schema ." * ), * @SWG\Parameter(name="client_type", type="integer", required=false, in="formData", * description="调用此接口的客户端类型: 1-Android, 2-IOS. 非必填,因此 required 写了 false" * ), * @SWG\Parameter(name="gender", type="integer", required=false, in="formData", * default="1", * description="性别: 1-男; 2-女. 注意这个参数的default上写的不是参数默认值,而是默认会被填写在swagger页面上的值,为的是方便用swagger就地访问该接口." * ), * ) */ public function loginAction() { // php code } /** * @SWG\Get(path="/User/myWebPage", tags={"User"}, * produces={"text/html"}, * summary="用户的我的网页", * description="这不是个api接口,这个返回一个页面,因此 produces 写了 text/html", * @SWG\Parameter(name="userId", type="integer", required=true, in="query"), * @SWG\Parameter(name="userToken", type="string", required=true, in="query", * description="用户令牌", * ), * ) */ public function myWebPageAction(){ // php code }
规则简单明了,看着代码你们就都懂了.不懂的话,去看文档吧...
上面 login 接口中用到了两个有结构的数据, 一个是 image 类型的数组, 一个是 video 类型的结构.
(其实结构化的参数只能在 in="body"
时才能够用,但这并不妨碍咱们为了简化问题,把结构化数据格式化为 json 当字符串传递. 咱们只要将这种结构展示在文档里就能够了)
这种有结构的东西 swagger 也能够用 php 注释定义:
<?php /** * @SWG\Definition(type="object", @SWG\Xml(name="Image")) */ class Image { /** * @SWG\Property() * @var string */ public $url; /** * @SWG\Property(format="int32") * @var int */ public $height; /** * @SWG\Property(format="int32") * @var int */ public $width; } <?php /** * @SWG\Definition(type="object", @SWG\Xml(name="Video")) */ class Video { /** * @SWG\Property() * @var string */ public $url; /** * @SWG\Property() * @var string */ public $thumb_url; /** * @SWG\Property(format="int32") * @var int */ public $length; /** * @SWG\Property(format="int64") * @var int */ public $size; }
这样当这两个类也被 swagger-php/bin/swagger 扫描到后,其余地方就能够正确引用到 Image 和 Video 为名字的这两个结构体了.
这样作的好处是,在接口参数文档中,这个结构会被展现出来,这样客户端同窗就知道该传什么结构了.
个人接口栗子里都没有写 response 规则,是由于咱们用 json 做为返回载体,返回错误码也是包含在这个 json 结构体里. 而且多数接口返回的json格式都很复杂,用 swagger 的 response 规则基本无法描述.
swagger 的 response 编写规则是按照 http 的 response code 来的 (404, 401 等), 总之对咱们的接口来讲,这套描述规则很差用.
所以我就直接舍弃了 response 描述, 直接用 swagger 就地请求接口看看返回了什么就是. 再不行就把接口的返回信息在 description 里大致描述一下.
文档写完后,就能够调用 swagger-php/bin/swagger 命令生成 swagger.json, 再拷贝到 swagger-ui 中你指定的那个目录中,就能够访问文档了.
NOTE:
你们应该已经看出来了,其实接口的注释不必定要写在接口上,凭空写注释,同样能生成文档.因此没必要纠结各个注释放在什么地方. 好比 swagger 总体定义, tag 定义等,写在任意能够被扫描到的 php 文件中就能够了.
这里只是本身理解加翻译的简要说明,更详细的字段说明,仍是要去看文档.再次贴出文档:
http://bfanger.nl/swagger-exp...
接口描述 (@SWGGet, @SWGPost 等) 经常使用字段:
summary - string 接口的简要介绍,会显示在接口标头上,不能超过120个字符 description - string 接口的详细介绍 externalDocs - string 外部文档连接 operationId - string 全局惟一的接口标识 consumes - [string] 接口接收的MIME类型 produces - [string] 接口返回的MIME类型,如 application/json schemes - [string] 接口所支持的协议,取值仅限: "http", "https", "ws", "wss" parameters - [Parameter Object | Reference Object] 参数列表
参数描述 (@SWGParameter) 经常使用字段:
name - string 参数名. 经过路径传参(in 取值 "path")时有注意事项,没用到,懒得看了... in - string 参数从何处来. 必填. 取值仅限: "query", "header", "path", "formData", "body" description - string 参数描述. 最好别太长 type - string 参数类型. 取值仅限: "string", "number", "integer", "boolean", "array", "file" required - boolean 参数是否必须. 经过路径传参(in 取值 "path")时必须为 true. default - * 默认值. 在你打算把参数经过 path 传递时规矩挺多,我没用到.用到的同窗本身看文档吧.
此段写于 2018-04-27. 本文其余段落成于 2017-01-12. 方法仅限 git 项目.
今天有朋友问 php swagger 的使用方法, 又审了一遍此文, 才发现没有写自动生成这部分.
每次更新项目还要手动生成文档的话, 就太麻烦了. 这种操做岂能不自动化. 方法以下:
前提: 项目为 git 项目. 项目部署以 git 为基础.
原理: 使用 git 的 hook 机制 在适当节点执行文档生成命令.
时机: 有两种: 若是使用 git 部署项目, 则能够在部署时自动生成; 若是不是, 也能够考虑在 git commit 时自动生成.
步骤:
首先找到须要被扫描 controller 所在目录, 假设其为 /project_path/app/controllers/
.
而后找到 swagger 生成的 json 格式文档应该放置的地方, 假设其为 /project_path/swagger/docs/
.
那么生成文档的命令以下:
php /path/to/swagger-php/bin/swagger /project_path/app/controllers/ -o /project_path/swagger/docs/
那么直接把上面这句命令放在对应的项目中的 .git/hooks/post-update
脚本中便可, 如没有则新建.
好吧, 上面那行命令是为了一眼能够看出这行命令是作什么的. 但它有个坏处, 就是要把项目的绝对路径写死在脚本里, 这样这个脚本就不具备通用性. 上面这行命令能够更新为下面的这行等价命令:
proj_root=$(readlink -f $(dirname $(git rev-parse --git-dir))) && \ php /path/to/swagger-php/swagger-php/bin/swagger $proj_root/app/controllers/ -o $proj_root/swagger/docs/
如此, 则每次该项目代码执行 git pull
命令时, swagger 文档将会自动更新.
swagger 牛X的地方就是它能够在文档上就地访问接口并展现输出,对于调试和对接口很是的方便.但若是不想将 swagger-ui 部署在接口项目下,那么在 swagger-ui 就地访问接口时,就会因跨域问题而请求不到结果. (Response Headers: no response from server; Response Body: no content).
这里不讲跨域问题怎么解决,只是给遇到上面的问题的各位一个思路,知道错误是由跨域产生的,就好解决了.
若是要将文档放在公网,直接暴露本身的接口可不太合适. 所以要给文档的访问地址作鉴权.
由于安全性要求不高,而且公司较小开发组人员很少,我就直接用了 nginx 提供的 http basic auth 作了登陆鉴权.
老规矩,先贴官方文档:
https://www.nginx.com/resourc...
这样作鉴权很简单,分分钟搞定. 不详述, 就两步:
$ htpasswd -cb your/path/to/api_project_accounts.db admin password_for_admin $ htpasswd -b your/path/to/api_project_accounts.db liuxu 123456 $ htpasswd your/path/to/api_project_accounts.db xiaoming
-c
选项表示若是帐号文件 ( api_project_accounts.db ) 不存在,则新建之. 所以建立第一个帐号时必定要加 -c, 以后建立则必定不要再加 -c.-b
参数表示明文指定密码,就是上面第一条和第二条命令中最后一个输入 (password_for_admin, 123456) . 所以若是不想明文指定,能够不加 -b, 像上面的第三条命令,就会和sudo命令同样,让你输入一段看不见的密码.
上面命令建立了三个帐号, admin, liuxu, xiaoming, 并将帐号密码保存在 api_project_accounts.db 文件中.
location /dist { auth_basic "my api project login"; auth_basic_user_file your/path/to/api_project_accounts.db; }
记得改完了重启 nginx:
nginx -s reload
这样一个简单的登陆鉴权就创建起来了.
NOTE:
若是系统里没有 htpass
这个命令, 能够安装 apache 的 httpd 服务器软件, htpass
这个命令就包含在其中:
yum install httpd
[1] swagger 官网
[2] swagger 项目地址
[3] swagger ui 项目地址
[4] swagger php 项目地址
[5] swagger 文档参考地址