在上一篇文章中,我们讲解了什么是 api,什么是 sdk:

https://www.cnblogs.com/tanshaoshenghao/p/16217608.html

今天将来到我们万丈高楼平地起系列文章的第二篇:如何编写 api 文档?

咳咳,其实写 api 文档这个事情也没有一个统一的标准,写这篇文章更多地是分享与记录自己的一些心得体会。

曾经有大佬和我说,通过一个人的 api 设计大概率能看出他的工程水平,并且推荐我去看一些优秀的 api 设计,比如 aws 的 api。

后来我感觉,学 api 有点像研习四书五经,入门后谁都能吟两句,至于能否真正消化理解及能发挥多大价值,不同人有不同的造化。

所以,或许写 api 就像比武功吧,一招一式耍出来后,花架子或许能唬住大众,高手之间的交流则只可意会不可言传......

呃不过阿菌可能连花架子也算不上,所以就不在大家面前卖弄如何设计 api 了。

但关于怎么写 api 文档这样的业务小常识,感觉还是可以分享一下的!

让人又爱又恨的 api 文档

首先我想说的是,脱离现实谈理想都是耍流氓,据我自己的切身体会,这世界上大概率是没有人喜欢写 api 文档的。

毕竟工作本来就累,这代码一写完,测试不找碴已经是万幸了,要顺便改上几个 bug,谁还记得维护 api 文档嘛。

但是咧,一旦我们需要去维护别人留下的代码,又或者需要翻看自己几个月前堆的屎山,立马就会哭爹喊娘:这兔崽子为啥不留 api 文档...... T_T

所以,为了方便自己,同时也能方便他人,我们还是要把编写 api 文档这件事情放在心上。

放在心上则意味着放在首位,也就是说,当我们开始一项开发任务的时候,首先要把 api 定义好,并且把 api 文档完善细化好,让文档先行!

api 文档一般是由服务端同学定义的,但最好能由前后端同学一起定初稿;一个人设计容易有疏漏与盲区,两个人一起设计往往双方都能得到进步。我自己在定义 api 的时候也常常会和前端同学交流一下想法,前端同学往往能给出一些建议,大家磨合后开发就会比较顺畅。(一开始可能会在开发的过程中改动初始设计,慢慢设计多了,返工的概率就减少了)

api 文档需要说明的内容

下面就和大家介绍一下为一个 api 编写文档需要包含哪些信息,我们以上一篇文章中的接口为例,也就是我们的云你好 api:

我在想有没有一种可能,等我的系列文章更新完后,云你好服务就正式上线商用了...... 别小瞧它只是云你好哦,阿菌会在这系列文章里分享所有自己在当前工作中的心得体会与思考,由浅入深,希望能给大家带来一点点启发,也欢迎各路大佬前来指点交流。

# 云你好服务 API 接口回顾:
@app.get("/api/v1/hello")
def hello():
# 看用户是否传递了参数,参数为打招呼的目标
name = request.args.get("name", "")
# 如果传了参数就向目标对象打招呼,输出 Hello XXX,否则输出 Hello World
return f"Hello {name}" if name else "Hello World"

首先我们要给出描述信息:

描述信息:云你好 api,大家可以通过云你好,获得一次来自云端的问候

然后要给出 api 的请求方式:

请求方式:GET

接着要给出请求地址:

http://127.0.0.1:5000/api/v1/hello

然后要给出请求参数:

然后我们要给出返回参数:

为了让 api 的调用者直接看懂,一定要记得给出响应示例,这里的响应示例不能只包括成功的请求响应,也要包括请求失败的响应:

状态码:200
"hello 阿菌" 状态码:400
"您的余额不足了,不能打招呼了" 状态码:500
"sorry~服务器开小差了"

简单来说就是要把调用 api 各种可能出现的情况告诉用户,我们顺便完善一下云你好的代码,让云你好服务可以容纳以上述三种情况:

# 云你好服务 API 接口
@app.get("/api/v1/hello")
def hello():
"""
首先判断用户的余额够不够
假设我们已经写好了判断逻辑,
后面我们讲 cookie,session,header 等知识的时候会补充
"""
try:
if not enough_money():
return "您的余额不足了,不能打招呼了", 400 # 看用户是否传递了参数,参数为打招呼的目标
name = request.args.get("name", "") # 如果传了参数就向目标对象打招呼,输出 Hello XXX,否则输出 Hello World
return f"Hello {name}" if name else "Hello World", 200 except Exception:
# 假设在使用云你好服务的过程中出现了异常
return "sorry~服务器开小差了", 500

请求响应设计小加餐

当然,云你好只是个极简的例子,企业开发中是不会直接返回字符串的,一般会约定一个数据格式,比如状态码为 200 返回:

{
"items": ["xxx", "yyy", "zzz", ...],
"total": 10
}

状态码非 200 的响应,也就是请求发生错误的响应数据格式会是这样的:

{
"msg": "sorry~ something went wrong..."
"msg_cn": "sorry~服务器开小差了..."
"error_code": "20000"
"detail": {
"xxx": "...."
}
}

刚开始学编程的时候我不太明白,既然已经有了响应码,比如 400,500,为什么还要弄一个 error_code 呢?

其实,error_code 一般是用来判断客户端错误的,比如说我们设定找不到资源的状态码为 404,但我们的逻辑中很可能会有好几段找数据的逻辑,比如用户传了用户名,地址,手机,邮箱,我们逐个到数据库中判断这些数据是否存在,如果都用 404 作为响应,而且提示信息都是"资源不存在",那么定位错误就比较麻烦了。

有了错误码,我们则可以根据每一种情况定义一个错误码,比如:

以下均为 404 错误
* 用户名不存在 - error_code: 10000
* 收货地址不存在 - error_code: 10001
* 手机号不存在 - error_code: 10002
* 邮箱不存在 - error_code: 10003

这样在开发联调定位错误的时候是非常方便的,而且我们还可以根据不同的状态码,设置不同的报错信息。

不过,写代码就像写文章,其实形式多种多样,没有对错,不必拘泥于一种形式。比如我见过只通过 error_msg 区分不同不同错误的,也挺好的。

如何让 api 文档看起来更舒服?

上面我们介绍了为一个 api 编写文档需要对外提供哪些信息,虽然我们知道了要写什么,但是离写出一个好看的 api 文档还是有一定距离的。

这里的距离一共包括以下几点:

  1. 我们每次写 api 文档时的心情不一样,有的时候我们心情特别好,写 api 文档就会特别认真,会详细且准确地描述每一个参数;但我们总会有心情不好的时候,为了在心情不好的时候也能对自己的行为做出约束,我们得想一些办法。
  2. api 文档的样式。如果仅仅用一个在线文档(甚至用word/pdf)写 api,我们自己定义的格式通常是比较普通的,为了让 api 文档的阅读者(比如我们自己)有一个好心情,我们得想办法弄个好看点的样式。
  3. api 文档给出的示例要是能够运行调试就更好了。

这个时候我们可以使用一些外部的 api 文档软件,帮助我们构建出一份漂亮的 api 文档。而 api 文档工具软件,业内首推的就是 swagger 了,免费又好用。

下面我分享一下我是如何给云你好服务接入 swagger api 文档的。

善用 Github 上的开源工具

由于云你好是用 python 的 flask 框架搭建的(选择 python 讲解是因为 python 代码就像说大白话,配合注释容易让大家看懂),当我们想要使用某项主流技术的时候,首先可以去 Github 上搜一下有没一些好用的脚手架,这样可以避免重复造轮子。

比如我们可以在 github 上搜索 flask swagger,然后选择按星星的数量排序。

可以看到,第一名的是一个基于 flask 二次开发的框架,虽然这个框架已经直接集成了 swagger,但是不太符合我们的需求,我们是希望能直接基于现有的云你好代码接入 swagger,而不是换一个新框架。

接着我们看第二个快 3k 星星的 flasgger 框架,点进去后直接看示例,看看如何能把软件跑起来。

看完后发现 flasgger 貌似非常符合我们的需求,只要额外添加几行代码就能跑起 swagger 了,于是我们把 swagger 接入我们的云你好服务:

from flask import Flask, request
from flasgger import Swagger, swag_from app = Flask(__name__)
swagger = Swagger(app) # 云你好服务 API 接口
@app.get("/api/v1/hello")
@swag_from('hello.yml')
def hello():
try:
if not enough_money():
return "您的余额不足了,不能打招呼了", 400 # 看用户是否传递了参数,参数为打招呼的目标
name = request.args.get("name", "") # 如果传了参数就向目标对象打招呼,输出 Hello XXX,否则输出 Hello World
return f"Hello {name}" if name else "Hello World", 200 except Exception:
# 假设在使用云你好服务的过程中出现了异常
return "sorry~服务器开小差了", 500 if __name__ == '__main__':
app.run()

swagger 可以通过 yml 格式描述一个 api 的详细信息,可以描述的 api 信息包括且不限于我们上文提到的 api 文档需要说明的内容:

云你好 api
---
parameters:
- name: name
in: query
type: string
responses:
200:
description: "请求成功"
schema:
examples: "Hello 阿菌"
type: string
400:
description: "客户端错误"
schema:
examples: "您的余额不足了,不能打招呼了"
type: string
500:
description: "服务端错误"
schema:
examples: "sorry~服务器开小差了"
type: string

然后我们可以把云你好服务运行起来,访问 http://localhost:5000/apidocs 后能得到一个这样的界面:

我们甚至可以在 swagger 界面上运行调试云你好的 api 接口:

那有了这份 api 文档之后,内部的开发协作就会变得非常方便,大家按照约定调用接口就能使用相应的服务了。

api 文档部署小加餐

api 文档其实是语言无关的,不管我们用的是 java、python、golang 什么语言都好,想要得到这样的一份 api 文档,关键在于编写好 yml 文件,描述好我们每一个 api 的用途,请求参数以及响应。

由于我们的云你好服务只是一个非常简单的服务端程序,为了演示方便,阿菌就直接把 swagger 跑在服务端了。

事实上,企业级应用是很少会这样部署 api 文档的,我先带大家看一下 flasgger 大概是怎么把 swagger 集成到我们的后端服务器的,我们可以从运行情况进行反推。

首先我们重启一次服务器,访问一次 http://localhost:5000/apidocs ,并且打开控制台,看一下后端发生了什么:

/Users/game-netease/Desktop/flaskProject/venv/bin/python -m flask run
* Serving Flask app 'app.py' (lazy loading)
* Environment: development
* Debug mode: off
* Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
127.0.0.1 - - [13/May/2022 08:48:33] "GET /apidocs/ HTTP/1.1" 200 -
127.0.0.1 - - [13/May/2022 08:48:33] "GET /flasgger_static/lib/jquery.min.js HTTP/1.1" 304 -
127.0.0.1 - - [13/May/2022 08:48:33] "GET /flasgger_static/swagger-ui-bundle.js HTTP/1.1" 304 -
127.0.0.1 - - [13/May/2022 08:48:33] "GET /flasgger_static/swagger-ui.css HTTP/1.1" 304 -
127.0.0.1 - - [13/May/2022 08:48:33] "GET /flasgger_static/swagger-ui-standalone-preset.js HTTP/1.1" 304 -
127.0.0.1 - - [13/May/2022 08:48:34] "GET /apispec_1.json HTTP/1.1" 200 -

从 flask 为我们打印的请求路径可以看出,flasgger 其实是把一系列 swagger 的静态资源集成到了我们的后端服务中,并且把对应的路由注册到了后端框架的路由表里,当我们访问 /apidocs/ 这样的路径时,它返回了相应的静态资源给浏览器渲染。

然而,大量的静态资源传输是会占用网络带宽的,在企业中,如果有大量的静态资源业务一般会配备有专门的静态资源服务,处理这类请求的服务一般对网络带宽要求比较高,对算力的要求相对会低一些。市面上也有不少和静态资源相关的技术服务,比如 CDN 内容分发网络,一些具备冷热存储功能的数据库服务等等。

所以,对于云你好服务来说,假设我们架设了静态资源服务器,更合理的 api 文档部署方案或许该是这样的:

好了,"云你好"服务第二集暂时就更新到这里了,这个系列的文章会比较长,希望在一步步搭建云你好服务的过程中,继续和大家分享我在写代码过程中的一些思考,下一期更精彩!

swagger在线api文档搭建指南,用于线上合适么?的更多相关文章

  1. WebApi生成在线API文档--Swagger

    1.前言 1.1 SwaggerUI SwaggerUI 是一个简单的Restful API 测试和文档工具.简单.漂亮.易用(官方demo).通过读取JSON 配置显示API. 项目本身仅仅也只依赖 ...

  2. 使用swagger来编写在线api文档

    swagger是一个非常简单,强大的框架.快速上手,只需要引入jar包 , 使用注解就可以生成一个漂亮的在线api文档 pom.xml <dependency> <groupId&g ...

  3. 基于.NetCore3.1搭建项目系列 —— 使用Swagger做Api文档 (上篇)

    前言 为什么在开发中,接口文档越来越成为前后端开发人员沟通的枢纽呢? 随着业务的发张,项目越来越多,而对于支撑整个项目架构体系而言,我们对系统业务的水平拆分,垂直分层,让业务系统更加清晰,从而产生一系 ...

  4. 基于.NetCore3.1搭建项目系列 —— 使用Swagger做Api文档 (下篇)

    前言 回顾上一篇文章<使用Swagger做Api文档 >,文中介绍了在.net core 3.1中,利用Swagger轻量级框架,如何引入程序包,配置服务,注册中间件,一步一步的实现,最终 ...

  5. springboot利用swagger构建api文档

    前言 Swagger 是一款RESTFUL接口的文档在线自动生成+功能测试功能软件.本文简单介绍了在项目中集成swagger的方法和一些常见问题.如果想深入分析项目源码,了解更多内容,见参考资料. S ...

  6. 【WebAPI No.4】Swagger实现API文档功能

    介绍: Swagger也称为Open API,Swagger从API文档中手动完成工作,并提供一系列用于生成,可视化和维护API文档的解决方案.简单的说就是一款让你更好的书写API文档的框架. 我们为 ...

  7. Swagger实现API文档功能

    介绍: wagger也称为Open API,Swagger从API文档中手动完成工作,并提供一系列用于生成,可视化和维护API文档的解决方案.简单的说就是一款让你更好的书写API文档的框架. 我们为什 ...

  8. 在ASP.NET Core Web API上使用Swagger提供API文档

    我在开发自己的博客系统(http://daxnet.me)时,给自己的RESTful服务增加了基于Swagger的API文档功能.当设置IISExpress的默认启动路由到Swagger的API文档页 ...

  9. Core Web API上使用Swagger提供API文档

    在ASP.NET Core Web API上使用Swagger提供API文档   我在开发自己的博客系统(http://daxnet.me)时,给自己的RESTful服务增加了基于Swagger的AP ...

随机推荐

  1. 学习Redis(二)

    1.Redis应用场景 1.缓存(键过期时间) 1) 缓存session会话 2) 缓存用户信息,找不到再去mysql查,查到然后回写到redis 3) 商城优惠卷过期时间 2.排行榜(列表& ...

  2. 插值方法 - Newton多项式(非等距节点)

    不多话.Nowton插值多项式(非等距节点)代码: 1 # -*- coding: utf-8 -*- 2 """ 3 Created on Wed Mar 25 15: ...

  3. 关于CPU、指令集、架构、芯片的一些科普

    作者:王强链接:https://zhuanlan.zhihu.com/p/19893066来源:知乎 随着智能设备的广泛普及,这几年媒体上越来越多的出现关于"架构""AR ...

  4. 关于sqlite数据库与sqlite studio

    今天使用了AS自带的sqlite实现了连接数据库,但是不能同步,比较麻烦,然后使用sqlite studio去设法实现同步,但是依旧无法创建成功,明天会继续调试.

  5. 数据库查询中where和having的用法

    1.类型: "baiWhere"是一个约束声明,在查询数据库du的结果返回之前对数据库中zhi的查询条件进行约束dao,即在结果返回之前起作用,且where后面不能使用" ...

  6. 微信jssdk分享(附代码)

    老规矩---demo图: (注释:微信分享只支持右上角三个点触发) ======> 老规矩上菜: 1. 这边我封装了  share.js function allSharefun(param) ...

  7. Mysql 8 使用过程中的命令记录

    Mysql 8 使用过程中的命令记录 注: 当前 MySQL 数据库的版本 8.0.27 修改密码 1. 使用其他用户修改root 密码 ALTER USER 'root'@'localhost' I ...

  8. js的split函数

    split() 方法用于把一个字符串分割成字符串数组. separator 必需.字符串或正则表达式,从该参数指定的地方分割 stringObject. ***如果把空字符串 ("" ...

  9. github ations 入门使用

    在使用之前,我们了解一下什么是 Github Actions. 在 GitHub Actions 的仓库中自动化.自定义和执行软件开发工作流程. 您可以发现.创建和共享操作以执行您喜欢的任何作业(包括 ...

  10. Java开发中关于资源路径获取问题

    描述 在开发中经常会读取配置文件,在Web开发中大多数都是在项目路径下.核心的API类或者是Controller异或是jsp页面等,基本都是基于web应用的相对路径,很少去操作绝对路径,但是在客户端. ...