我们在上一篇的简易教学文章中,用户登录接口和用户退出接口的response返回字段,都有一个code
字段和msg
字段,
response:{
code:{...},
msg:{...}
}
实际上,很有可能,我们整个系统的接口,都遵循同样的标准,所有接口都有code
字段和msg
字段,那么,每个地方的body和response都去写岂不是很麻烦;另外,可能我们整个网站绝大部分的接口都是要登录的,只有很少的一小部分不需要登录,每个都去设置也很麻烦啊。我承认就是懒,所以Panda api提供了一个简单的全局设置方法。
打开_settings.json5
文件
// _settings.json5
{
project_name: "Panda Api", /* 项目名称 */
project_desc: "Panda Api is a simple api manage tool" /* 项目简述 */
}
我们增加global
字段,内容变为:
// _settings.json5
{
project_name: "Panda Api",
project_desc: "Panda Api is a simple api manage tool",
global: {
apis:{
auth: true,
response:{
code:{name:"返回的状态码", type:"number"},
msg:{name:"返回的状态说明"}
}
},
}
}
这样我们设置了全局所有api接口的auth
字段全部为true
,全部都有权限验证,全局的response
都有了字段code
和msg
,我们可以打开我们的接口文档来查看http://localhost:9000
当然,我们的用户登录接口/login
是不需要验证用户登录的,我们可以在用户登录接口中单独设置auth
为false
,这就像继承的概念一样。
如果某一个请求的response
返回中不需要code
或者不需要msg
,我们也可以去掉。
{
name:"用户登录",
desc:"用户登录成功,接口会返回一个token",
method: "POST",
url:"/login/",
auth:false, /* 单独设置登录接口不需要验证和用户登录状态 */
body_mode:"json",
body:...
code
字段和msg
字段,如果有的地方不需要这两个字段,就不能简单的像auth字段一样修改一个值,你要做的是再不需要的地方设置删除。
response:{
code:"$del", /* 删除继承得来的code字段 */
msg:"$del", /* 删除继承得来的msg字段 */
...
}
这个时候设置了值为$del
的字段就没有了,我们可以刷新前端文档看一下。 或者也可以这么写:
response:{
code:{$del:true}, /* 删除继承得来的code字段 */
msg:{$del:true}, /* 删除继承得来的msg字段 */
...
}
如果平时我们调试的时候,想让某个设置的字段不出现,也可以用这个方法.例如:
body:{
title:{name:"文章标题"},
summary:{name:"文章摘要"},
category_id:{name:"文章分类id", type:"number"},
author_name:{name:"文章作者姓名"},
source:{name:"文章来源", $del:true}, // 标记为不要source字段
tag:{name:"文章标签", $del:true} // 标记为不要标签字段
},
有时候,几个接口的字段差不多,又只是几个接口会这样,是不能用全局设置来表示的,比如用户操作的文章列表的字段、文章添加的字段、文章查看的字段差不多,如果字段很多,每个都重复写就很累,虽然可以复制粘贴,但是维护的时候又要同时维护多个地方,带来了麻烦。
所以Panda api上我参考swagger设计了$ref
语法,方便的引用数据模型,此外又增加了$exclude
语法、$include
语法和继承语法,让我们使用起来更方便,我们来看例子吧。
我们的文章的接口文档是这样的:
{
name:"文章接口",
apis:[
{
name:"文章添加/编辑",
url:"/post/add/",
body:{
id:{name:"文章id", desc:"添加提交的时候是没有id, 编辑提交的时候才有", type:"PosInt"}, required:false}, // PosInt 表示正整数
title:{name:"文章标题"},
summary:{name:"文章摘要", type:"csummary"}, // csummary表示中文一小段文字
category_id:{name:"文章分类id", type:"PosInt"},
author_name:{name:"文章作者姓名", type:"cname"}, // cname表示中文姓名
source:{name:"文章来源"},
tag:{name:"文章标签"}
},
response:{
/* code 和 msg 字段来自全局了 */
id: {name:"文章id"}
}
},
{
name:"查看文章",
method:"GET",
url:"/post/{id:\\d+}/",
query:{
page:{name:"分页", type:"PosInt"} // PosInt 表示正整数
},
response:{
/* code 和 msg 字段来自全局了 */
total_page: {name:"总页数", type:"PosInt"},
total_count: {name:"总记录数", type:"PosInt"},
current_page: {name:"当前页码", type:"PosInt"},
result:
[{
id:{name:"文章id", desc:"添加提交的时候是没有id, 编辑提交的时候才有", type:"PosInt"},
title:{name:"文章标题"},
summary:{name:"文章摘要", type:"csummary"},
category_name:{name:"文章分类名称"},
author_name:{name:"文章作者姓名", type:"cname"},
source:{name:"文章来源"},
tag:{name:"文章标签"},
read_count:{name:"阅读次数", type:"PosInt"},
created:{name:"创建时间", type:"PosInt"},
create_username:{name:"文章创建人姓名"}
}]
}
},
{
name:"文章列表",
method:"POST",
url:"/post/star/list/",
body:{
page:{name:"分页", type:"number"}
},
response:{
total_page: {name:"总页数", type:"number"},
total_count: {name:"总记录数", type:"number"},
current_page: {name:"当前页码", type:"number"},
result:
[{
id:{name:"文章id", desc:"添加提交的时候是没有id, 编辑提交的时候才有", type:"PosInt"}, /* 这时id是必有的 */
title:{name:"文章标题"},
category_name:{name:"文章分类名称"},
author_name:{name:"文章作者姓名", type:"cname"},
tag:{name:"文章标签"},
created:{name:"创建时间"},
}]
}
}
]
}
我们的文章接口文档里面一共有三个接口:文章添加,文章查看,文章列表,
如果以文章添加为基础来看的话,文章查看与文章添加的区别是:id变为了必填,category_id变为了category_name,增加了3个字段read_count
阅读次数,created
创建时间,create_username
文章创建人姓名,
title:{name:"文章标题"},
summary:{name:"文章摘要"},
category_id:{name:"文章分类id", type:"number"},
author_name:{name:"文章作者姓名"},
source:{name:"文章来源"},
tag:{name:"文章标签"}
这个部分的字段是一模一样的,
再看文章列表,id
,title
,author_name
,tag
,是一样的,category_name
、author_name
、created
是增加字段。
实际上,这些新增的字段也都是文章模型的属性,相同的不同字段分开写就导致如果发生字段调整,属性调整,要维护的地方就会很多,容易出错。
所以Panda api设计的是,我们可以统一设计一个模型,然后每次调用的时候,只调用需要的部分。这样写和维护就统一只在一个地方。
创建文件夹 _data
, 在_data
文件夹中创建文件models.json5
(文件名可以根据自己的需要设定),在models.json5
中敲下面的代码:
{
Article:{
id:{name:"文章id", desc:"添加提交的时候是没有id, 编辑提交的时候才有", type:"number"}, /* 这时id是必有的 */
title:{name:"文章标题"},
summary:{name:"文章摘要"},
category_id:{name:"文章分类id", type:"number"},
category_name:{name:"文章分类名称"},
author_name:{name:"文章作者姓名"},
source:{name:"文章来源"},
tag:{name:"文章标签"},
read_count:{name:"阅读次数", type:"number"},
created:{name:"创建时间", type:"number"},
create_username:{name:"文章创建人姓名"}
}
}
我们的文章接口可以这么改写:
{
name:"文章接口",
apis:[
{
name:"文章添加/编辑",
url:"/post/add/",
body:{
$ref: "./_data/models.json5:Article",
$exclude:["category_name", "read_count", "created", "create_username"],
id:{name:"文章id", desc:"添加提交的时候是没有id, 编辑提交的时候才有", type:"number", required:false}
},
response:{
/* code 和 msg 字段来自全局了 */
id: {name:"文章id"}
}
},
{
name:"查看文章",
method:"POST",
url:"/post/{id:\\d+}/",
body:{
page:{name:"分页", type:"number"}
},
response:{
/* code 和 msg 字段来自全局了 */
total_page: {name:"总页数", type:"number"},
total_count: {name:"总记录数", type:"number"},
current_page: {name:"当前页码", type:"number"},
result:
[{
$ref: "./_data/models.json5:Article",
$exclude:["category_id"]
}]
}
},
{
name:"文章列表",
method:"POST",
url:"/post/star/list/",
body:{
page:{name:"分页", type:"number"}
},
response:{
total_page: {name:"总页数", type:"number"},
total_count: {name:"总记录数", type:"number"},
current_page: {name:"当前页码", type:"number"},
result:
[{
$ref: "./_data/models.json5:Article",
$include:["id", "title", "category_name", "author_name", "tag", "created"]
}]
}
}
]
}
$include
和$exclude
语法在文件添加接口中,我们使用$ref
引用了文章模型,语法是:./_data/models.json5:Article
就是文件路径,后面是冒号:
,后是具体的模型引用路径,如果只引用一个title字段,也可以这么写:$ref:"./_data/models.json5:Article/title"
,
接着有的字段我们是不要的,我们就可以使用$exclude
语法,排除一些字段,$exclude:["category_name", "read_count", "created", "create_username"],
,放入$exclude
中的字段就不会被加载进来。
可以排除对象或数组的嵌套属性
, 可以用/
标识字段路径来删除,例如, 假设$ref
的文章模型里面还有嵌套的对象和数组
Article:{
id: {name:"用户id", type:"number"},
title: {name:"文章标题"},
category: {
id:{name:"分类id", type:"number"},
name:{name:"分类名称", type:"string"}
},
tags:[{
id:{name:"标签id"},
title:{name:"标签内容"}
}]
}
假设我们$ref
这个Article
, 不要category
对象的id
, 不要tags
对象列表的id
.可以这么写: $exclude:["category/id", tags/0/id]
:
...
{
name:"文章添加/编辑",
url:"/post/add/",
body:{
$ref: "./_data/models.json5:Article",
$exclude:["category/id", "tags/0/id", "created"],
},
response:{
id: {name:"文章id"}
}
},
...
我们会得到下面的数据:
...
{
name:"文章添加/编辑",
url:"/post/add/",
body:{
id: {name:"用户id", type:"number"},
title: {name:"文章标题"},
category: {
name:{name:"分类名称", type:"string"}
},
tags:[{
title:{name:"标签内容"}
}]
},
response:{
id: {name:"文章id"}
}
},
...
我们在/post/add/
接口,重写了id
字段,id:{name:"文章id", desc:"添加提交的时候是没有id, 编辑提交的时候才有", type:"number", required:false}
。当把字段在当前重写了,那么就会以当前字段为准。
可以重写对象或数组的嵌套属性
,如果我们想重写嵌套到对象或数组中的属性,可以这么写:
Article:{
id: {name:"用户id", type:"number"},
title: {name:"文章标题"},
category: {
id:{name:"分类id", type:"number"},
name:{name:"分类名称", type:"string"}
},
tags:[{
id:{name:"标签id"},
title:{name:"标签内容"}
}]
}
如果我们$ref
了Article
, 并且要重写category
对象的id
属性, 同时为category
对象增加一个order
, 为tags
对象增加一个order
, 可以这么写:
...
{
name:"文章添加/编辑",
url:"/post/add/",
body:{
$ref: "./_data/models.json5:Article",
"category/id":{name:"新的分类id", type:"PosInt"},
"category/order":{name:"分类排序", type:"int"},
"tags/0/order":{name:"标签排序", type:"int"}
},
response:{
id: {name:"文章id"}
}
},
...
这么写以后,就会重写了category
对象的id
属性, 并为category
对象增加order
,为tags
对象增加order
. 变成下面的样子:
...
{
name:"文章添加/编辑",
url:"/post/add/",
body:{
id: {name:"用户id", type:"number"},
title: {name:"文章标题"},
category: {
id:{name:"新的分类id", type:"PosInt"}, // 修改
name:{name:"分类名称", type:"string"},
order:{name:"分类排序", type:"int"} // 新增
},
tags:[{
id:{name:"标签id"},
title:{name:"标签内容"},
order:{name:"标签排序", type:"int"} // 新增
}]
},
response:{
id: {name:"文章id"}
}
},
...
文章列表中,我们一样的引入了Article
模型,然后没用$exclude
,而是用$include
语法指定我们需要的字段,这么的好处是,当我们再Article模型中增加了字段,增加的字段不会在模型自动加载出来,因为系统只会加载$include
里面的字段;但是$exclude
语法因为一开始没有设置这个新增加的字段,所以在$exclude
模型的情况,模型Article
中增加的字段会自动加载出来。
如果同时使用了$include
和$exclude
字段,那么先加载$include
中设置的字段,然后再删除掉$exclude
中设置的字段.
所以一共是四步操作:
我们还可以只引入某一个字段,比如有一个简单的文章列表接口,只需要文章标题和文章id,
{
name:"简要文章列表",
method:"POST",
url:"/post/simple/list/",
body:{
page:{name:"分页", type:"number"}
},
response:{
total_page: {name:"总页数", type:"number"},
total_count: {name:"总记录数", type:"number"},
current_page: {name:"当前页码", type:"number"},
result:
[{
id:{$ref: "./_data/models.json5:Article/id"},
title:{$ref: "./_data/models.json5:Article/title"}
}]
}
}
每个地方都要去写./_data/models.json5
这么一长段路径,很麻烦,万一路径变了,那么每个地方都要改。因此我们可以使用define语法。来定义路径,所以,我们的文章接口改进未这样:
{
name:"文章接口",
define:{
model: "./_data/models.json5",
article: "./_data/models.json5:Article"
},
apis:[
{
name:"文章添加/编辑",
url:"/post/add/",
body:{
$ref: "$model:Article",
$exclude:["category_name", "read_count", "created", "create_username"],
id:{name:"文章id", desc:"添加提交的时候是没有id, 编辑提交的时候才有", type:"number", required:false},
},
response:{
/* code 和 msg 字段来自全局了 */
id: {name:"文章id"}
}
},
{
name:"查看文章",
method:"POST",
url:"/post/{id:\\d+}/",
body:{
page:{name:"分页", type:"number"}
},
response:{
/* code 和 msg 字段来自全局了 */
total_page: {name:"总页数", type:"number"},
total_count: {name:"总记录数", type:"number"},
current_page: {name:"当前页码", type:"number"},
result:
[{
$ref: "$article",
$exclude:["category_id"]
}]
}
},
{
name:"文章列表",
method:"POST",
url:"/post/star/list/",
body:{
page:{name:"分页", type:"number"}
},
response:{
total_page: {name:"总页数", type:"number"},
total_count: {name:"总记录数", type:"number"},
current_page: {name:"当前页码", type:"number"},
result:
[{
$ref: "$article",
$include:["id", "title", "category_name", "author_name", "tag", "created"]
}]
}
},
{
name:"简要文章列表",
method:"POST",
url:"/post/simple/list/",
body:{
page:{name:"分页", type:"number"}
},
response:{
total_page: {name:"总页数", type:"number"},
total_count: {name:"总记录数", type:"number"},
current_page: {name:"当前页码", type:"number"},
result:
[{
id:{$ref: "$article/id"},
title:{$ref: "$article/title"}
}]
}
}
]
}
查看文章接口,接口地址是这么写的:"url":"/post/{id:\\d+}/"
,
{
name:"查看文章",
method:"POST",
url:"/post/{id:\\d+}/",
这么写的意思是:我们匹配的url是/post/id/
,然后我们对id
进行了约束,只要id
是数字就会匹配,我也可以写单词匹配,/post/{id:\\w+}/
,然后也可以写更复杂的正则匹配。
有的接口,会在多个地方去体现和是用,比如,用户操作有图片上传,文章上传有图片,后台管理员操作也有上传图片,或者多个角色都有上传图片,但是我们开发的时候,需要把文档放到一起。
所有复用的东西需要创建在_data
目录,我们在_data
里面,创建文件common_api.json5
(文件名可以根据我们的实际需求修改),里面的代码是:
{
photo_upload:{
name:"上传照片",
desc:"这个接口既可以上传处方单扫描件、本地处方单照片,还可以上传身份证扫描件和本地身份证照片",
url:"/common/photo/upload/",
method:"POST",
body_mode:"form-data",
body: {
file: {name:"要上传的文件", desc:"如果是扫描件要转变为扫描好的本地文件", type:"image"},
file_type:{name:"文件类型", desc:"分为处方单,身份证", $enum:[["id_no","身份证"], ["chufangdan", "处方单"], ["other", "其它普通照片"]]}
},
response:{
$ref:"./_data/models.json5:Response",
result:{
upload_id:{name:"上传文件id", desc:"上传文件保存后的返回id", type:"number"},
upload_url:{name:"上传文件后访问的url地址", $value:"$body/file:url"},
ocr:{name:"OCR识别数据", desc:"上传文件系统自动识别OCR返回的结果,目前只有部分处方单和身份证有这个数据", required:false}
}
},
test_data:[{
body:{file:"aaa.jpg", file_type:"id_no"},
response:{
code: 1,
msg: "",
result: {
upload_id: 1,
upload_url: "/_upload/aaa.jpg",
ocr:{id_no:"533025199002160013", name:"张三", gender:"男", age:"29"}
}
}
}]
}
}
当我们定义好这个公共接口后,我们就可以在文章中直接引用这个接口
{
name:"文章接口",
define:{
model: "./_data/models.json",
article: "./_data/models.json:Article"
},
apis:[
{
$ref:"./_data/common_api.json:photo_upload",
name:"照片上传"
},
{
name:"文章添加/编辑",
url:"/post/add/",
body:{
$ref: "$model:Article",
$exclude:["category_name", "read_count", "created", "create_username"],
id:{name:"文章id", desc:"添加提交的时候是没有id, 编辑提交的时候才有", type:"number", required:false},
},
response:{
/* code 和 msg 字段来自全局了 */
id: {name:"文章id"}
}
},
...
]
}
同样的,继承过来以后还是可以自定义里面的字段属性,比如这里name
字段就进行了自定义,这里主要还有就是写上name
方便维护的时候看
在照片上传的接口中,有两个特殊的地方,首先:我们的body_mode
是form-data
,也必须是form-data
才会进行文件的接收。
在test_data
中,body
中的上传文件的匹配用的是文件名进行匹配,
test_data
的返回值response
中,我们还可以获取文件的访问url
地址,url
访问地址为:_upload/
+ 文件名。即如果我们的文件名是:aaa.jpg
, 那么我们访问的文件url地址是:/_upload/aaa.jpg
。当你访问这个地址: http://localhost:9000/_upload/aaa.jpg
时,就可以访问到文件。
我们也可以在response
的定义中,根据body
直接获取上传文件的url。例如:
...
body: {
file: {name:"要上传的文件", desc:"如果是扫描件要转变为扫描好的本地文件", type:"image"},
file_type:{name:"文件类型", desc:"分为处方单,身份证", $enum:[["id_no","身份证"], ["chufangdan", "处方单"], ["other", "其它普通照片"]]}
},
response:{
result:{
upload_id:{name:"上传文件id", desc:"上传文件保存后的返回id", type:"number"},
upload_url:{name:"上传文件后访问的url地址", $value:"$body/file:url"},
ocr:{name:"OCR识别数据", desc:"上传文件系统自动识别OCR返回的结果,目前只有部分处方单和身份证有这个数据", required:false}
}
},
...
$value:"$body/file:url"
的意思就是 upload_url
这个字段的值是通过$body
中的file
字段的可访问的url
来进行输出。
正在搞...
2020-03-01 05:41