flask-siwadoc是一个兼具数据校验和openapi(swagger)文档自动生成的项目
只需要初始化一个siwa=SiwaDoc(app)
,利用装饰器 siwa.doc()
修饰flask视图函数,即可将该视图对应的路由加入openapi的paths中。
可以将请求参数放置在 query
、path
、header
、cookie
、body(json、formdata)
5种不同的地方,完全支持openapi规范所定义的5种参数方式。
基于pydantic
,请求参数可自动转换为对应的数据类型
flask-siwadoc内置了swagger
(默认)、redoc
、rapidoc
等多种UI界面
配置参数:
- SIWA_USER:登录用户名
- SIWA_PASSWORD: 登录密码
只有同时设置了用户名和密码访问文档才需要登录权限,该场景用在正式环境中,防止接口文档被匿名访问
pip install flask-siwadoc
from flask import Flask
from flask_siwadoc import SiwaDoc
app = Flask(__name__)
siwa = SiwaDoc(app, title="siwadocapi", description="一个自动生成openapi文档的库")
# 或者使用工厂模式
# siwa = SiwaDoc(title="siwadocapi", description="一个自动生成openapi文档的库")
# 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 就可以看到openapi文档页面
from pydantic import BaseModel, Field
USERS = [
{"username": "siwa1", "id": 1},
{"username": "siwa2", "id": 2},
{"username": "siwa3", "id": 3},
]
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)
keyword: str = None
@app.route("/users", methods=["GET"])
@siwa.doc(query=QueryModel, tags=["user"], group="user")
def users_list(query: QueryModel):
"""
user list
"""
print(query.page) # 1
return {"data": USERS[:query.size]}
以查询参数方式接收数据时,例如:/path?page=1&size=10
- 定义一个继承自
pydantic.BaseModel
的子类:QueryModel
@siwa.doc(query=QueryModel)
:doc
装饰器中接收名为query
的对象,用于在文档中展示参数列表users_list(query: QueryModel)
视图函数中定义名字为query
的参数,主要是方便开发者直接通过query
对象获取参数值
class TokenModel(BaseModel):
token: str
@app.route("/me", methods=["GET"])
@siwa.doc(header=TokenModel, tags=['auth'], group='admin')
def param_in_header():
token = request.headers.get("token")
print("token:", token)
return {"token": token}
header中的参数可直接通过 request.headers.get
获取
class CookieModel(BaseModel):
foo: str
@app.route("/cookie", methods=["GET"])
@siwa.doc(cookie=CookieModel, tags=['auth'], group='admin')
def param_in_cookie():
foo = request.cookies.get("foo")
print("foo:", foo)
return {"foo": foo}
以请求body接收数据时,例如:
curl -X 'POST' \
'http://127.0.0.1:5000/user/login' \
-H 'accept: */*' \
-H 'Content-Type: application/json' \
-d '{
"password": "string",
"username": "string"
}'
class LoginModel(BaseModel):
username: str
password: str
@app.route("/user/login", methods=["POST"])
@siwa.doc(body=LoginModel, tags=['auth'])
def user_login(body: LoginModel):
return {
"username": body.username,
"password": body.password,
"id": 1}
- 定义一个继承自
pydantic.BaseModel
的子类:LoginModel
@siwa.doc(body=LoginModel)
:doc
装饰器中接收名为body
的对象,用于在文档中展示参数列表users_list(body: LoginModel)
视图函数中定义名字为body
的参数,方便开发者直接通过body
对象获取参数值
example5中使用json
作为请求体,当请求文件等复杂数据类型时,需要使用formdata
class UserModel(BaseModel):
id: int
username: str
@app.post('/form')
@siwa.doc(form=UserModel)
def test_form(form: UserModel):
print(form.id)
print(form.username)
return form.username
- 定义一个继承自
pydantic.BaseModel
的子类:UserModel
@siwa.doc(form=UserModel)
:doc
装饰器中接收名为form
的对象,用于在文档中展示参数列表test_form(form: UserModel)
视图函数中定义名字为form
的参数,方便开发者直接通过form
对象获取参数值
@app.post('/form_with_files')
@siwa.doc(form=UserModel, files={'file1': {"required": True, "single": False}, 'file2': {"required": False, "single": True}})
def test_form_with_files(form: UserModel, files: dict):
print(form.id)
print(form.username)
print(files.keys())
return form.username
- 定义一个继承自
pydantic.BaseModel
的子类:UserModel
@siwa.doc(form=UserModel, files={'file1': {"required": True, "single": False}, 'file2': {"required": False, "single": True}})
:doc
装饰器中接收名为form
的对象、名为files
的字典。form
对象用于在文档中展示参数列表,files
字典用于定义文件参数列表以及文件参数配置(required
配置此文件参数是否必传,默认False
;single
配置此文件参数接收单文件还是文件列表,默认True
)test_form_with_files(form: UserModel, files: dict)
视图函数中定义名字为form
的参数,方便开发者直接通过form
对象获取基本数据类型的参数值;files
字典用于获取文件对象
@app.post('/form_only_files')
@siwa.doc(form=BaseModel, files={'file1': {"required": True, "single": False}, 'file2': {"required": False, "single": True}})
def test_form_only_files(files: dict):
print(files.keys())
return 'success'
@siwa.doc(form=BaseModel, files={'file1': {"required": True, "single": False}, 'file2': {"required": False, "single": True}})
:doc
装饰器中接收名为form
的对象、名为files
的字典。form
对象指定为基类BaseModel
,files
同情形二test_form_only_files(files: dict)
视图函数中定义名字files
的字典,用于获取文件对象
需要告诉客户端接口返回的字段时,指定参数resp
class UserModel(BaseModel):
id: int
username: str
@app.route("/users", methods=["GET"])
@siwa.doc(query=QueryModel, resp=UserModel)
def users_list(query: QueryModel):
"""
user list
"""
return {"data": USERS[:query.size]}
- 定义一个继承自
pydantic.BaseModel
的子类:UserModel
@siwa.doc(resp=UserModel)
:doc
装饰器中接收名为resp
的对象,用于在文档中展示返回的字段列表
项目中如果接口太多,我们可以对接口根据业务划分不同的模块标签来分类管理。
@siwa.doc(resp=UserModel, tags=["user"])
指定tags
参数,tags参数是一个列表,一个接口可支持多个标签。
除了可以指定标签外,我们还可以指定分组
@app.route("/admin/login", methods=["POST"])
@siwa.doc(body=LoginModel, resp=UserModel, tags=['auth'], group='admin')
def admin_login(body: LoginModel):
return {"username": body.username, "id": 1}
完整示例可参考 example.py
文档默认使用swagger
进行渲染,你可以在路径上指定参数?ui=swagger
切换成 swagger
渲染文档。
http://127.0.0.1:5000/docs/?ui=swagger
数据校验报错时,flask-siwadoc 会抛出异常flask_siwadoc.error.ValidationError
,ValidationError 继承自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
reference
- https://pydantic-docs.helpmanual.io/
- https://github.com/tiangolo/fastapi
- https://github.com/bauerji/flask-pydantic
- https://github.com/kemingy/flaskerk
任何问题欢迎发issue或者加我微信 lzjun567 交流,欢迎PR, 如果对你有帮助或者给你的工作带来了极大的便利,可考虑赞赏作者