Flask 应用集成 Swagger UI

成熟的 Web  API 框架总有一款 API 文档与之伴随,当前最知名的莫过于支持 Open API 的 Swagger 了。Python 的 Flask 框架支持 Swagger UI 也有几条路子

  1. Flasgger : 好像是 flask-swagger 的 fork
  2. flask-swagger: 许久未更新了,不用考虑
  3. flask-restful-swagger: 到目前也两年未更新了
  4. Flask-RESTPlus 的 Swagger 特性: 真需要用到 Flask-RESTPlus 就可以用它

单纯用 Flask 构建 API 的话,细数起来也就 Flasgger 比较合适,如果甩开 Flask 而用 FastAPI 的话,就不用操心 Swagger 了,因为 FastAPI 原生的支持 Swagger。

本文中我们将体验如何使用 Flasgger, 关于使用方法,在它源码的 README.md 已经描述的很清楚了。Flasgger 提供了以下几种主要的使用方式

  1. 用函数的 docstrings 注释
  2. 使用外部 YAML 文件
  3. 使用 YAML 文件的字典形式变量描述
  4. 用 Marshmallow Schemas

用 docstrings 的方式在 IDE 中无法得到语法高亮,写错了也不知道,而且大段的 docstrings 影响阅读有效的代码。用外部的 YAML 文件能实现 API 文档与有效代码的分离。字典形式描述的 YAML 和外部 YAML 文件并没有多大区别,字典变量可以声明到单独的 Python 文件中。

现在讨论使用哪种方式还为时过早,先来瞧瞧以上几种基本的方式的实例(Marshmallow 的方式对代码的侵入性较大, 此处跳过)

Flasgger 得到一个 Swagger UI

用 pip 安装 flasgger

$ pip install flasgger

然后写一个 Flask app main.py

运行该 main.py 后,就可以打开 http://localhost:5000/apidocs

Swagger UI 里目前是空的,只是启动了一个 Swagger UI 空壳而已。接下来开始看如何在其中加上 API

docstrings 注解 API

直接借用官方的例子,自己想来想去也没觉得有比官方更好的例子。在 main.py 中加一个 API

运行它后,再次访问 http://localhost:5000/apidocs,看到下面的样子

可以 Try it out。有兴趣的请直接查看 http://localhost:5000/apispec_1.json 文档的内容

使用外部 YAML 文件

觉得在函数中加一大段注释影响阅读主体代码,或者会一不小心修改到 docstrings 内容的话,可使用外部 YAML 文件。要做的基本就是先把前面 docstring 的内容保存成一个 colors.yml(只对描述作修改) 文件

colors.yml

然后 colors 函数去掉 docstring 注释, 加上 @swag_from 装饰来引用 colors.yml 文件

重新运行  main.py 后得到一样的结果(描述部分不同)

不想用装饰器的话,还能 docstrings 和 yml 文件结合,写成

但是,为何不用装饰器呢?

@swag_from 目前只支持 yaml 和 yml 文件,以后将会对 json, py 文件的支持。如果想支持用 JSON 文件格式,那需要第三方的包自动转换为 yml 格式。

再进入到字典格式 API 描述之前,我们先来考虑一个问题,如果有两个 API 返回了相同结构的数据,在新的 yml 文件中声明一次就重复了,问题就是多个 API 中如何重用 Swagger Model。

查看 JSON 文档 http://localhost:5000/apispec_1.json

请拿这个与 colors.yml 对比理解 yml 中的内容是如何映射到  apispec_1.josn 中的。如果再来一个新的 API 同时引用 colors.yml  会怎么样呢?在 main.py 中加上 color1 函数

这时时候 http://localhost:5000/apidocs 可看到两个 APIs

  1. GET /color/{palette}
  2. GET /color1/{palette}

再查看 http://localhost:5000/apispec_1.json, 除了 paths 下新增了 color1/{palette} 外, definitions 中的内容与原来一样,所以 Flasgger 是能正确处理 request/response 中用到的 schema 的定义的。可以想见

  1. 一个 yml 文件可以引用另一个 yml 文件中的 definitions 的 Model 定义
  2. 不同 API 引用 yml 中的 definitions 最终会被合并到一处

于是我们可以这么实现

  1. 被共享的 Model 定义放到同一个 yml 文件中,要保证该 yml 会被 @swag_from 引用
  2. 每个 API 定义自己专享的部分,所用公共 Model 以 schema: $ref: '#/definitions/Palette' 方式引用

如此,这般仍然无法避免产生众多的 yml 文件,于是下一种字典的方式或许是更好的选择

使用字典变量描述

由于字典是 Python 中的变量,也就有了更大的灵活性。比如现在仍然有两个 API, color 和  color1,它们有共享的 Model, 我们建立 apis.py 文件,内容为

因为是 Python 代码,可以把公共的代码轻松抽取出来放到一块,如上面的 shared_definitions

main.py 中引用它们

用字典的方式避免了产生很多的 yml 文件,所有的 API spec 定义可以选择放到同一个或多个 py 中, 它们之间共享内容非常简单。

字典方式相比而言变成了更优的选择。

总结

通过对以上几种用法的体验,最后作一个总结,以供我们选择何种方式作参考

  1. docstrings 方式,大面积的注解侵入了代码,影响对业务代码的阅读,同时 docstrings 中的内容不能被 IDE 高亮显示
  2. yml 让 API 描述分离了出来,但 API 多的话会产生同样多的  yml 文件,而且 yml 间共享内容不那么容易
  3. 字典方式可以把 API spec 放在一个或多个 py 文件中,共享相同的 Model, request, response 都很便利

不管使用以上何种方,都要求我们对 Swagger API(或 Open API) 文档的语法有一定的熟悉。而在 API 函数中使用的 docstring 或 @swag_from 只是帮助我们获得了 URL Path 和 HTTP Method。我们最终想要得到的就是那个 http://localhost:5000/apispec_1.json, 这样的话,完全可以自己编辑 apispec_1.json, 只须 Flask 同时启动一个  Swagger UI 来浏览该 apispec。

Python 是无类型的语言,如果想要 Swagger  以某种机制来获得参数和返回类型的话,那应该是 type hints.

链接:

  1. FLASK PYTHON: CREATING REST APIS AND SWAGGER DOCUMENTATION
  2. Working with APIs using Flask, Flask-RESTPlus and Swagger UI

类别: Flask. 标签: , . 阅读(84). 订阅评论. TrackBack.
guest
0 Comments
Inline Feedbacks
View all comments
trackback

[…] Flask 实现 Swagger UI 文档功能,基本上要让 Flask 配合 Flasgger, 所以写了篇 Flask 应用集成 Swagger UI。然而不断的 Google 过程中偶然间发现了 FastAPI 这么一款集成了 Swagger UI […]

0
Would love your thoughts, please comment.x
()
x