是什么
flask-openapi3是一个基于Flask的WEB API框架,设计灵感来自于FastAPI,使用pydantic验证数据,自动生成Swagger UI和Redoc两种在线API文档。
为什么
python WEB已经有很多成熟的知名框架,Flask、Django、FastAPI…,为什么还要自己开发一个呢?首先我是一个热衷于Flask后端开发的爱好者,它是一个那么简单、优雅、python范儿的一个轻量级框架,各种插件:REST、数据库、表单验证、swagger文档…,在REST API开发过程中,我遇到了一些问题:
- REST API插件有flask-restful和flask-restplus,其中flask-restplus支持swagger文档生成和表单验证,但基于Resource的API设计对REST执行的过于严格,显得不是那么灵活。
- 最知名的swagger文档插件非flasgger莫属了,但它很强的代码侵入性给文档编写和代码阅读造成了一定的影响,不可否认的是在这之前我的项目中就是使用的flasgger。
- WTForms很好的解决了表单验证的问题,但对json数据的解析却不是那么简单,当然你可以重构其表单解析和验证机制。
归于以上原因,我决定开始flask-openapi3。
怎么用
像Flask一样
你可以像使用Flask一样来使用,当然你可能并不想这么做。
from flask_openapi3 import OpenAPI
app = OpenAPI(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run()
一个简单的例子
Flask 2.x已经实现了REST API的编写方式,像app.get
、app.post
、app.put
、app.delete
,但是它仅仅是app.route
的快捷方式,参考此设计这里实现了OpenAPI
,它能接收info
参数——生成swagger文档的必要信息,并给每一个API提供一个tags
参数(给API分类),更进一步支持security
(安全验证)、responses
(响应体验证)等参数。
为视图函数设计了path
、query
、form
、body
、header
、cookie
六个参数,来支持不同类型的参数——强大的pydantic
库为参数验证提供了有力的支撑。
from pydantic import BaseModel
from flask_openapi3 import OpenAPI
from flask_openapi3.models import Info, Tag
info = Info(title='book API', version='1.0.0')
app = OpenAPI(__name__, info=info)
book_tag = Tag(name='book', description='图书')
class BookData(BaseModel):
age: int
author: str
@app.get('/book', tags=[book_tag])
def get_book(query: BookData):
"""get books
get all books
"""
return {
"code": 0,
"message": "ok",
"data": [
{"bid": 1, "age": query.age, "author": query.author},
{"bid": 2, "age": query.age, "author": query.author}
]
}
if __name__ == '__main__':
app.run(debug=True)
REST API
接下来展示一个完整的REST API例子:
from typing import Optional
from pydantic import BaseModel, Field
from flask_openapi3 import OpenAPI
from flask_openapi3.models import Info, Tag
from flask_openapi3.models.security import HTTPBearer
info = Info(title='book API', version='1.0.0')
securitySchemes = {"jwt": HTTPBearer(bearerFormat="JWT")}
app = OpenAPI(__name__, info=info, securitySchemes=securitySchemes)
book_tag = Tag(name='book', description='图书')
security = [{"jwt": []}]
class Path(BaseModel):
bid: int = Field(..., description='图书id')
class BookData(BaseModel):
age: Optional[int] = Field(..., ge=2, le=4, description='年龄')
author: str = Field(None, min_length=2, max_length=4, description='作者')
class BookDataWithID(BaseModel):
bid: int = Field(..., description='图书id')
age: Optional[int] = Field(None, ge=2, le=4, description='年龄')
author: str = Field(None, min_length=2, max_length=4, description='作者')
class BookResponse(BaseModel):
code: int = Field(0, description="状态码")
message: str = Field("ok", description="异常信息")
data: BookDataWithID
@app.get('/book/<int:bid>', tags=[book_tag], responses={"200": BookResponse}, security=security)
def get_book(path: Path, query: BookData):
"""获取图书
根据图书id获取图书
"""
return {"code": 0, "message": "ok", "data": {"bid": path.bid, "age": query.age, "author": query.author}}, 522
@app.get('/book', tags=[book_tag])
def get_books(query: BookData):
"""get books
get all books
"""
return {
"code": 0,
"message": "ok",
"data": [
{"bid": 1, "age": query.age, "author": query.author},
{"bid": 2, "age": query.age, "author": query.author}
]
}
@app.post('/book', tags=[book_tag])
def create_book(body: BookData):
print(body)
return {"code": 0, "message": "ok"}
@app.put('/book/<int:bid>', tags=[book_tag])
def update_book(path: Path, body: BookData):
print(path)
print(body)
return {"code": 0, "message": "ok"}
@app.delete('/book/<int:bid>', tags=[book_tag])
def delete_book(path: Path):
print(path)
return {"code": 0, "message": "ok"}
if __name__ == '__main__':
app.run(debug=True)
项目地址
源码地址:点击这里
同时也提供一个完整的项目示例