3

flask-siwadoc:一个自动生成openapi接口文档的库

 2 years ago
source link: https://foofish.net/flask-siwadoc.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在前后端分离变得越来越流行的今天,大家的职责也更加明确与专注,后端负责提供API接口,前端负责UI与交互。前后端基于接口文档进行沟通。

但是程序员最讨厌两件事,一是讨厌自己写文档,另一件是讨厌别人不写文档。

所以对后端来说,理想情况是代码写完,文档已经自动生成好了。openapi(就是早前的swagger)已成为事实上的接口文档规范指南,既然都有规范出现,那么基于这份规范来实现一个自动生成的openapi的接口文档库就不是难事了。

所以我就写了这样一个库

名字叫flask-siwadoc ,它是一个兼具 数据校验和openapi(swagger)文档自动生成的项目。 这两个特性都是写restful接口刚需痛点功能。

现来说说这个库的一些特点:

0、零配置

接入flask-siwadoc无需任何配置, 只需要创建一个SiWaDoc实例,即可使用,没有复杂的配置。 当然,在后面的版本,会开放相关的配置接口得以让开发者根据自己的需求来配置openapi。

1、数据校验

flask-siwadoc 站在巨人肩膀上,数据校验利用pydantic强大的数据验证功能,支持请求查询参数请求体参数的数据校验及转换功能。因此本项目同时依赖于pydantic。

2、接口文档自动生成

接口文档生成只需要简单初始化一个siwa=SiwaDoc(app),利用装饰器 siwa.doc()修饰flask的视图函数,即可将该视图函数加入openapi的接口文档规范中。

3、ui切换

flask-siwadoc内置了redocswaggerrapidoc等多种UI界面,通过参数/docs/?ui=xxx切换

4、文档支持分组与标签

对于大型项目,接口分组和标签显得尤为重要,不然前端很难找到相关接口。

pip install flask-siwadoc

现在来看个简单例子:

example 1

初始化SiwaDoc支持两种方式,一种直接将Flask实例传入SiwaDoc的构造方法,另一种是使用工厂模式进行初始化。

from flask import Flask
from flask_siwadoc import SiwaDoc

app = Flask(__name__)

siwa = SiwaDoc(app)


# or
# siwa = SiwaDoc()
# siwa.init_app(app)

@app.route("/hello", methods=["GET"])
@siwa.doc()
def hello():
    return "hello siwadoc"


if __name__ == '__main__':
    app.run()

运行后,访问 http://127.0.0.1:5000/docs 就可以看到该接口的文档页面

20220605014223.png

对于大部分接口来说,我们要对请求参数进行校验,有的参数来源于url的查询参数,有的是以application/json格式作为请求体的参数。我们只需要基于Pydantic定义好对应的Model数据结构,该结构一方面可以用来做数据校验,另一方面可用于生成openapi所需的schema。

example 2:指定查询参数 query

from pydantic import BaseModel, Field


class QueryModel(BaseModel):
    page: int = Field(default=1, title="current page number")
    size: int = Field(default=20, title="size of page", ge=10, le=100)


@app.route("/users", methods=["GET"])
@siwa.doc(query=QueryModel, tags=['user'])
def users_list():
    """
    user list
    """
    return [{"username": "siwa", "id": 1}]

对于查询接口,GET请求需如果有查询参数,得益于pydantic强大的类型功能,我们只需要一个继承BaseModel的自定义类,即可实现数据校验及转换。

如何在视图函数中使用该query这个对象呢? 有两种方式

@app.route("/users", methods=["GET"])
@siwa.doc(query=QueryModel, tags=["user"])
def hello():
    print(request.query.keyword)
    return "hello"

flask-siwadoc 将QueryModel的实例对象query绑定到了flask的request对象上,不过对开发者来说使用并不友好,从代码上你没法知道他是什么类型么,IDE也无法用.的方式调出该实例的属性。你只有看了flask-siwadoc的文档或者源码才知道是怎么回事。

方式二:(推荐方式)

@app.route("/users", methods=["GET"])
@siwa.doc(query=QueryModel, tags=["user"])
def hello(query: QueryModel):
    print(query.keyword)
    return "hello"

query变量作为视图函数的参数,flask-siwadoc 会自动将QueryModel实例对象注入到该函数一个叫query的变量中,我们可以给query指定类型声明,因此通过IDE可以很方便的调出实例属性。开发者一眼也能看出他的类型。

下面的example3中的body参数原理也是类似的。

20220604201906.png

example3 :指定请求体 body

class LoginModel(BaseModel):
    username: str
    password: str


@app.route("/login", methods=["POST"])
@siwa.doc(body=LoginModel)
def login(body: LoginModel):
    return {"username": body.username, "id": 1}

对于POST请求,请求体一般是基于application/json 类型, flask-siwadoc会自动将数据转换成LoginModel类型的对象,就可以像静态语言一样,通过点的方式调出属性,例如 body.username

20220605014442.png

example4: 指定返回体 resp

对于接口文档来说,除了要告诉前端需要传什么参数之外,还需要告诉他们返回的结构是什么,同样也只需要定义一个Model,写明有什么字段,字段对应的类型是什么即可。在doc装饰器中赋值给resp变量。

class UserModel(BaseModel):
    id: int
    username: str


@app.route("/users/1", methods=["GET"])
@siwa.doc(resp=UserModel)
def users():
    """
    user detail
    """
    return {"username": "siwa", "id": 1}
20220605012328.png

example5: 指定标签分类 tags

项目中如果接口太多,我们可以对接口根据业务划分不同的模块标签来分类管理,我们只需要在装饰器 siwa.doc 指定tags参数

@siwa.doc(resp=UserModel, tags=["user"])
...

指定tags参数,tags参数是一个列表,一个接口可支持多个标签。

20220606075152.png

example6:路径参数也支持文档化

除了查询参数和请求体参数外,对于url路径中的参数,例如/users/<int(min=1):user_id>,这是flask的路由语法,指users后面是一个必须大于1的整数,生成文档时,不需要开发者做额外的处理,flask-siwadoc内部经过处理,直接将参数反映在接口文档中。

class QueryModel(BaseModel):
    gender: str


class UpdatePasswordModel(BaseModel):
    password: str


@app.route("/users/<int(min=1):user_id>", methods=["POST"])
@siwa.doc(query=QueryModel, body=UpdatePasswordModel, resp=UserModel)
def update_password(user_id):
    """
    update password
    """
    return {"username": "siwa", "id": user_id}
20220605014105.png

完整示例可参考example.py

文档默认使用redoc进行渲染,你可以通过指定参数ui=swaggerui显式文档。

http: // 127.0
.0
.1: 5000 / docs /?ui = swagger
20220604203420.png

如果数据校验报错,flask-siwadoc 会抛出异常flask_siwadoc.error.ValidationErrorValidationError异常继承自pydantic.ValidationError

class QueryModel(BaseModel):
    keyword: str


@app.route("/users", methods=["GET"])
@siwa.doc(query=QueryModel, tags=["user"])
def hello(query: QueryModel):
    print(query)
    return "hello"

该接口中,keyword是必选的查询参数,如果url中没有keyword参数,就会抛出异常

    raise ValidationError(e)
flask_siwadoc.error.ValidationError: 2 validation errors for Auth
    username
field required (type=value_error.missing)
password
field required (type=value_error.missing)

这时候,我们可以通过使用flask的 errorhandler() 装饰函数来注册ValidationError错误,异常就可以被validate_error函数捕获,开发者可以给前端直接返回一个友好的错误响应体

@app.errorhandler(ValidationError)
def validate_error(e: ValidationError):
    return dict(code=-1, msg="请求参数错误", error_info=e.errors()), 400
20220604214851.png

github地址:https://github.com/lzjun567/flask-siwadoc

任何问题欢迎发issue或者加我微信 lzjun567 交流,欢迎PR

有问题可以扫描二维码和我交流

关注公众号「Python之禅」,回复「1024」免费获取Python资源

python之禅

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK