第一步:安装swagger-ui前端
用cd命令进入到存放静态文件的目录,例如public目录。执行下面命令:
git clone https://github.com/swagger-api/swagger-ui.git
注意:上面的命令,下载的是当前最新版本3.0,但是3.0有个已知的问题,不支持中文。如果想支持中文,需要指定版本。
git clone --branch v2.2.10 https://github.com/swagger-api/swagger-ui.git
第二步:安装swagger-php后端
进入tp框架找到根目录下,打开composer.json找到require项,添加一行,然后使用更新命令。
"zircote/swagger-php": "*"
注意,每行用逗号分隔,不要忘了。
或者执行命令:
composer require "zircote/swagger-php"
注意:这个命令默认下载的是当前最新的版本,也就是3.x。我到git上查了一下,想要跟swagger-ui的2.x版本配合使用,需要使用swagger-php 2.x版本。指定版本:
composer require "zircote/swagger-php:2.0.13"
第三步:生成swagger.json文件
1、教程上,让执行下面命令(实际执行的命令,要根据你那边的目录来确定)
php E:/WampServer/WWW/tpSwagger/tp5/vendor/zircote/swagger-php/bin/swagger E:/WampServer/WWW/tpSwagger/tp5/vendor/zircote/swagger-php/Examples -o E:/WampServer/WWW/tpSwagger/tp5/swaggerApi/swagger.json
第1个路径是你安装成功后组件的路径;
第2个路径是你想要生成这个目录下所有用swagger方式注释的php文件,把所有注释生成api文档;
第3个路径是你存放生成swagger.json的路径。
可能是我这边默认安装的是新版的swagger(查看版本是3.0),提示找不到swagger
我到bin这个目录,发现没有swagger文件,但是有一个openapi文件。
于是就把bin/swagger改为bin/openapi,再次执行。
结果虽然报了很多警告,但是确实生成了json文件。
2、swagger-ui加载生成的json文件
进入到swagger-ui的下载目录,找到dist目录,打开里面的index.html文件,修改文件引用的.json文件的路径为你的json文件的路径(就是上面生成的那个swagger.json)
如果json文件的目录设置不对,则会提示Failed to load API definition.
3、编写控制器方法生成swagger.json:
如果我们每次修改了api,还要手动执行第三步的代码,有些繁琐,那我们就在控制器中写一个方法,每次访问swagger-ui的时候自动执行,然后跳转到前台swagger界面中。
快速更新文档
<?php namespace app\index\controller; use think\Controller; class Index extends Controller { public function index(){ $path = 'D:/WampServer/WWW/tpSwagger/tp5/application'; //你想要哪个文件夹下面的注释生成对应的API文档 $swagger = \OpenApi\scan($path); // header('Content-Type: application/json'); // echo $swagger; $swagger_json_path = 'D:/WampServer/WWW/tpSwagger/tp5/swaggerApi/swagger.json'; $res = file_put_contents($swagger_json_path, $swagger); if ($res == true) { $this->redirect('http://localhost/tpSwagger/swagger-ui/dist/index.html'); } } }
我这边在使用Swagger方法的时候,不能用这个\OpenApi\scan($path);提示找不到方法,需要用\Swagger\scan($path);
扫描的结果是对象,如果要写入文件,需要转换为字符串。另外在写入文件的时候,遇到权限问题。我最后没有使用file_put_contents函数
$path = APP_PATH.'portal/test'; //你想要哪个文件夹下面的注释生成对应的API文档 $swagger = \Swagger\scan($path); // header('Content-Type: application/json'); // echo $swagger; $swagger_json_path = ROOT_PATH.'public/swaggerApi/swagger.json'; // 检测模板目录 $dir = dirname($swagger_json_path); if (!is_dir($dir)) { mkdir($dir, 0755, true); } $myfile = fopen($swagger_json_path, "w") or die("Unable to open file!"); $swagger=json_encode($swagger, true); fwrite($myfile, $swagger); fclose($myfile);
第四步:编写swagger注释
控制器的注释写法
/** * @SWG\Resource( * basePath="http://skyapi.dev", * resourcePath="/vps", * @SWG\Api( * path="/vps", * @SWG\Operation( * method="GET", * type="array", * summary="Fetch vps lists", * nickname="vps/index", * @SWG\Parameter( * name="expand", * description="Models to expand", * paramType="query", * type="string", * defaultValue="vps,os_template" * ) * ) * ) * ) */ class VpsController extends Controller { // ... }
这只是个简单的实例具体的注释写法请自己百度
2、swagger注释使用
参考这个(写的比较全面):https://learnku.com/laravel/t/7430/how-to-write-api-documents-based-on-swagger-php#747b67
还有这个:https://blog.csdn。net/dyt19941205/article/details/79025266(链接中‘。’换成‘.’访问哈,不然发表不了)
这个:https://www.jianshu.com/p/554cd3762ab1
结合上面这几篇文章学习写了一个借口文档,也就四个方法,基本需要的东西都有了,以后再写文档可以照搬了
写接口文档真的很费时间,尤其写在php注释里,比较简单的文档可以使用swagger提供的编辑工具,直接在上面修改json文件后导出使用就可以了:http://editor.swagger.io/
下面给一个完整的接口注释:
<?php namespace app\app\controller;use think\Controller;use think\Request;use think\Db;/** * @SWG\Swagger( * schemes={"http"}, * host="127.0.0.1", * basePath="/restudy/public/index.php/app/", * consumes={"multipart/form-data","X-Requested-With/XMLHttpRequest"}, * produces={"application/json"}, * @SWG\Info( * version="1.0", * title="我的测试学习api项目", * description="接口学习,目前只写了一个Api类,把顶级栏目接口、二级栏目接口、文章列表接口、文章内容接口写到了一起" * ), * * @SWG\Tag( * name="Api", * description="新闻webapp四大接口", * ), * ) */class Api extends Controller { /** *@SWG\Get(path="/api/gettopnav", tags={"Api"}, * summary="获取顶级栏目列表", * description="获取顶级栏目列表,返回栏目id,名称,和是否启用栏目", @SWG\Parameter( * description="ajax请求要加上X-Requested-With:XMLHttpRequest头字段", * format="string", * in="header", * name="X-Requested-With", * required=true, * type="string", default="XMLHttpRequest", * * ), @SWG\Response(response="200", description="操作成功", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=200, * ), * @SWG\Property( * property="msg", * type="string", example="顶级栏目返回成功", * ), @SWG\Property( * property="data", * type="object", example="[{cate_id:5,cate_name:'栏目1',cate_ison:1},{cate_id:6,cate_name:'栏目2',cate_ison:0}]", * ), * * )), @SWG\Response(response="201", description="数据为空", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=201, * ), * @SWG\Property( * property="msg", * type="string", example="数据为空!", * ), * * )), @SWG\Response(response="400", description="非法请求,不是ajax请求", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=400, * ), * @SWG\Property( * property="msg", * type="string", example="非法请求", * ), * * )), * ) */ //顶级栏目接口 public function getTopnav(){ if(request()->isAjax()){ $data=Db::table('re_cate')->where('cate_pid',0)->field('cate_id,cate_name,cate_ison')->select(); if(!empty($data)){ return json(['code'=>200,'msg'=>'顶级栏目返回成功','data'=>$data]); }else{ return json(['code'=>201,'msg'=>'数据为空!']); } }else{ return json(['code'=>400,'msg'=>'非法请求']); } } /** *@SWG\Post(path="/api/getsonnav", tags={"Api"}, * summary="获取二级栏目列表", * description="根据栏目id获取二级栏目列表,返回栏目id,名称,和是否启用栏目", @SWG\Parameter( * description="ajax请求要加上X-Requested-With:XMLHttpRequest头字段", * format="string", * in="header", * name="X-Requested-With", * required=true, * type="string", default="XMLHttpRequest", * * ), * @SWG\Parameter(name="id",type="integer", required=true, in="formData", * description="顶级栏目id" * ), @SWG\Response(response="200", description="操作成功", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=200, * ), * @SWG\Property( * property="msg", * type="string", example="二级栏目返回成功", * ), @SWG\Property( * property="data", * type="object", example="[{cate_id:5,cate_name:'栏目1',cate_ison:1},{cate_id:6,cate_name:'栏目2',cate_ison:0}]", * ), * * )), @SWG\Response(response="201", description="数据为空", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=201, * ), * @SWG\Property( * property="msg", * type="string", example="数据为空!", * ), * * )), @SWG\Response(response="400", description="非法请求,不是ajax请求", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=400, * ), * @SWG\Property( * property="msg", * type="string", example="非法请求", * ), * * )), * ) */ //二级栏目接口 public function getSonnav(){ if(request()->isAjax()){ $cate_id=input('id'); $data=Db::table('re_cate')->where('cate_pid',$cate_id)->field('cate_id,cate_name,cate_ison')->select(); if(!empty($data)){ return json(['code'=>200,'msg'=>'二级栏目返回成功','data'=>$data]); }else{ return json(['code'=>201,'msg'=>'无二级栏目']); } }else{ return json(['code'=>400,'msg'=>'非法请求']); } } /** *@SWG\Post(path="/api/getarticlelist", tags={"Api"}, * summary="获取文章列表", * description="根据栏目id获取文章列表(需要参数:栏目id,页码,一页显示文章数量)", @SWG\Parameter( * description="ajax请求要加上X-Requested-With:XMLHttpRequest头字段", * format="string", * in="header", * name="X-Requested-With", * required=true, * type="string", default="XMLHttpRequest", * * ), * @SWG\Parameter(name="id",type="integer", required=true, in="formData", * description="栏目id" * ), @SWG\Parameter(name="p",type="integer", required=true, in="formData", * description="页码" * ), @SWG\Parameter(name="num",type="integer", required=true, in="formData", * description="每页文章数量" * ), @SWG\Response(response="200", description="操作成功", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=200, * ), * @SWG\Property( * property="msg", * type="string", example="文章列表返回成功", * ), @SWG\Property( * property="data", * type="object", example="{total:null,per_page:5,current_page:1,last_page:null,data:[{ar_id:1,cate_id:10,ar_title:'文章标题一',ar_keywords:'文章关键字',ar_pic:'url',ar_content:'文章内容',ar_ison:1}]}", * ), * * )), @SWG\Response(response="201", description="数据为空", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=201, * ), * @SWG\Property( * property="msg", * type="string", example="数据为空!", * ), * * )), @SWG\Response(response="400", description="非法请求,不是ajax请求", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=400, * ), * @SWG\Property( * property="msg", * type="string", example="非法请求", * ), * * )), * ) */ //指定栏目文章列表(需要参数:栏目id,页码,一页显示文章数量) /* 返回json格式 { "code": 200, "msg": "文章返回成功", "data": { "total": null, "per_page": "1", "current_page": 1, "last_page": null, "data": [ { "ar_id": 1, "cate_id": 10, "ar_title": "title", "ar_keywords": "k,e,y", "ar_pic": "public/static/uploads/20190902\\98edeb3d27fe4342da8b07d5ae3e98de.jpg", "ar_content": "<p>内容<br/></p>", "ar_ison": 1 } ] } } */ public function getArticlelist(){ if(request()->isAjax()){ $cate_id=input('id'); $page=input('p'); $number=input('num'); $data=Db::table('re_article')->where('cate_id',$cate_id)->paginate($number,true,['page'=>$page]); if(!empty($data)){ return json(['code'=>200,'msg'=>'文章返回成功','data'=>$data]); }else{ return json(['code'=>201,'msg'=>'没有数据了']); } }else{ return json(['code'=>400,'msg'=>'非法请求']); } } /** *@SWG\Post(path="/api/getarticlecontent", tags={"Api"}, * summary="获取文章内容", * description="根据文章id获取文章信息", @SWG\Parameter( * description="ajax请求要加上X-Requested-With:XMLHttpRequest头字段", * format="string", * in="header", * name="X-Requested-With", * required=true, * type="string", default="XMLHttpRequest", * * ), * @SWG\Parameter(name="id",type="integer", required=true, in="formData", * description="文章id" * ), @SWG\Response(response="200", description="操作成功", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=200, * ), * @SWG\Property( * property="msg", * type="string", example="文章返回成功", * ), @SWG\Property( * property="data", * type="object", example="{ar_id:1,ar_title:'文章1',ar_pic:'url',ar_keywords:'文章关键字',ar_content:'文章内容',ar_ison:1}", * ), * * )), @SWG\Response(response="201", description="数据为空", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=201, * ), * @SWG\Property( * property="msg", * type="string", example="数据为空!", * ), * * )), @SWG\Response(response="400", description="非法请求,不是ajax请求", @SWG\Schema( * required={""}, * @SWG\Property( * property="code", * type="integer", example=400, * ), * @SWG\Property( * property="msg", * type="string", example="非法请求", * ), * * )), * ) */ //获取指定文章内容 public function getArticlecontent(){ if(request()->isAjax()){ $ar_id=input('id'); $data=Db::table('re_article')->where('ar_id',$ar_id)->find(); if(!empty($data)){ return json(['code'=>200,'msg'=>'文章返回成功','data'=>$data]); }else{ return json(['code'=>201,'msg'=>'无此文章']); } }else{ return json(['code'=>400,'msg'=>'非法请求']); } } }