Flask:如何自动化 OpenAPI v3 文档?
Flask: how to automate OpenAPI v3 documentation?
我需要记录一个用纯 Flask 2 编写的 API,我正在寻找执行此操作的统一方法。
我找到了不同的可行解决方案,但作为 Python 和 Flask 的新手,我无法在其中进行选择。我找到的解决方案是:
- https://github.com/marshmallow-code/apispec
- https://github.com/jmcarp/flask-apispec
- https://github.com/marshmallow-code/flask-smorest
为了分离不同的 API 端点,我使用了 Flask 蓝图。
MWE的结构如下:
我首先定义了两个简单的领域对象,Author 和 Book.
# author.py
class Author:
def __init__(self, id: str, name: str):
self.id = id
self.name = name
# book.py
class Book:
def __init__(self, id: str, name: str):
self.id = id
self.name = name
接下来,我使用两个单独的蓝图为它们创建了一个简单的 GET 端点。
# author_apy.py
import json
from flask import Blueprint, Response
from domain.author import Author
author = Blueprint("author", __name__, url_prefix="/authors")
@author.get("/")
def authors():
authors: list[Author] = []
for i in range(10):
author: Author = Author(str(i), "Author " + str(i))
authors.append(author)
authors_dicts = [author.__dict__ for author in authors]
return Response(json.dumps(authors_dicts), mimetype="application/json")
和
# book_api.json
import json
from flask import Blueprint, Response
from domain.book import Book
book = Blueprint("book", __name__, url_prefix="/books")
@book.get("/")
def books():
books: list[Book] = []
for i in range(10):
book: Book = Book(str(i), "Book " + str(i))
books.append(book)
books_dicts = [book.__dict__ for book in books]
return Response(json.dumps(books_dicts), mimetype="application/json")
最后我只是在Flask app下注册了两个蓝图
# app.py
from flask import Flask
from api.author.author_api import author
from api.book.book_api import book
app = Flask(__name__)
app.register_blueprint(author, url_prefix="/authors")
app.register_blueprint(book, url_prefix="/books")
@app.get('/')
def hello_world():
return 'Flask - OpenAPI'
if __name__ == '__main__':
app.run()
完整的源代码也可以在 GitHub 上找到。
考虑到这个最小的工作示例,我想知道自动生成 OpenAPI v3 yaml/JSON 文件的最快方法是什么,例如暴露在 /api-doc.yaml 端点上。
PS:这是我第一次 API 使用 Python 和 Flask。我正在尝试重现我可以用 Spring-Boot and SpringDoc
做的事情
我鼓励您将您的项目切换到 FastAPI,它与 Flask 没有太大区别或更难。
FastAPI docs about generating OpenAPI schema
它不仅可以让您轻松生成 OpenAPI 文档/规范。它也是异步的,速度更快,更现代。
另请参阅 FastAPI Alternatives, Inspiration and Comparisons 以了解差异。
尤其是上面 link 的引用应该可以解释为什么做你尝试做的事情可能不是最好的主意:
Flask REST frameworks
There are several Flask REST frameworks, but after investing the time
and work into investigating them, I found that many are discontinued
or abandoned, with several standing issues that made them unfit.
按照从 Flask 迁移到 FastAPI 的建议,我试了一下并重写了 Flask-Example of the question. The source code is also available on GitHub.
项目的结构几乎相同,但有一些附加功能可用(例如 CORS 中间件):
域的模型略有不同并扩展了 BaseModel from Pydantic。
# author.py
from pydantic import BaseModel
class Author(BaseModel):
id: str
name: str
和
# book.py
from pydantic import BaseModel
class Book(BaseModel):
id: str
name: str
FastAPI 等同于 Flask Blueprint 是 APIRouter。
下面是作者的两个控制器
# author_api.py
from fastapi import APIRouter
from domain.author import Author
router = APIRouter()
@router.get("/", tags=["Authors"], response_model=list[Author])
def get_authors() -> list[Author]:
authors: list[Author] = []
for i in range(10):
authors.append(Author(id="Author-" + str(i), name="Author-Name-" + str(i)))
return authors
和书籍
# book_api.py
from fastapi import APIRouter
from domain.book import Book
router = APIRouter()
@router.get("/", tags=["Books"], response_model=list[Book])
def get_books() -> list[Book]:
books: list[Book] = []
for i in range(10):
books.append(Book(id="Book-" + str(i), name="Book-Name-" + str(i)))
return books
重要的是要注意 API 端点的响应模型是使用 Python 类型定义的,这要归功于 Pydantic。然后将这些对象类型转换为 OpenAPI 文档的 JSON 模式。
最后我简单地registered/included FastAPI 对象下的APIRouters 并添加了CORS 的配置。
# app.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from domain.info import Info
from api.author.author_api import router as authors_router
from api.book.book_api import router as books_router
app = FastAPI()
app.include_router(authors_router, prefix="/authors")
app.include_router(books_router, prefix="/books")
app.add_middleware(CORSMiddleware,
allow_credentials=True,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/", response_model=Info)
def info() -> Info:
info = Info(info="FastAPI - OpenAPI")
return info
生成的 OpenAPI 文档可在端点 /openapi.json
访问,而 UI(又名 Swagger UI、Redoc)可在 /docs
访问
和/redoc
综上所述,这是自动生成的 JSON 格式的 OpenAPI v3 文档,可用于轻松生成其他语言的 API 客户端(例如使用OpenAPI-Generator tools).
{
"openapi": "3.0.2",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/authors/": {
"get": {
"tags": [
"Authors"
],
"summary": "Get Authors",
"operationId": "get_authors_authors__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response Get Authors Authors Get",
"type": "array",
"items": {
"$ref": "#/components/schemas/Author"
}
}
}
}
}
}
}
},
"/books/": {
"get": {
"tags": [
"Books"
],
"summary": "Get Books",
"operationId": "get_books_books__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response Get Books Books Get",
"type": "array",
"items": {
"$ref": "#/components/schemas/Book"
}
}
}
}
}
}
}
},
"/": {
"get": {
"summary": "Info",
"operationId": "info__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Info"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Author": {
"title": "Author",
"required": [
"id",
"name"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
}
}
},
"Book": {
"title": "Book",
"required": [
"id",
"name"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
}
}
},
"Info": {
"title": "Info",
"required": [
"info"
],
"type": "object",
"properties": {
"info": {
"title": "Info",
"type": "string"
}
}
}
}
}
}
为了启动应用程序,我们还需要一个用于生产的 ASGI 服务器,例如 Uvicorn or Hypercorn。
我使用 Uvicorn 并使用以下命令启动应用程序:
uvicorn app:app --reload
然后可以在您机器的 8000 端口上使用。
如果您想坚持使用 Flask,swagger-gen
是一个可以生成 full-featured 规范且实现开销非常低的库。
from swagger_gen.lib.wrappers import swagger_metadata
from swagger_gen.lib.security import BearerAuth
from swagger_gen.swagger import Swagger
from flask import Flask, request
app = Flask(__name__)
@app.route('/api/hello/say', methods=['GET'])
@swagger_metadata(
summary='Sample endpoint',
description='This is a sample endpoint')
def test():
return {'message': 'hello world!'}
swagger = Swagger(
app=app,
title='app')
swagger.configure()
if __name__ == '__main__':
app.run(debug=True, port='5000')
完全披露:我是作者。
我需要记录一个用纯 Flask 2 编写的 API,我正在寻找执行此操作的统一方法。 我找到了不同的可行解决方案,但作为 Python 和 Flask 的新手,我无法在其中进行选择。我找到的解决方案是:
- https://github.com/marshmallow-code/apispec
- https://github.com/jmcarp/flask-apispec
- https://github.com/marshmallow-code/flask-smorest
为了分离不同的 API 端点,我使用了 Flask 蓝图。 MWE的结构如下:
我首先定义了两个简单的领域对象,Author 和 Book.
# author.py
class Author:
def __init__(self, id: str, name: str):
self.id = id
self.name = name
# book.py
class Book:
def __init__(self, id: str, name: str):
self.id = id
self.name = name
接下来,我使用两个单独的蓝图为它们创建了一个简单的 GET 端点。
# author_apy.py
import json
from flask import Blueprint, Response
from domain.author import Author
author = Blueprint("author", __name__, url_prefix="/authors")
@author.get("/")
def authors():
authors: list[Author] = []
for i in range(10):
author: Author = Author(str(i), "Author " + str(i))
authors.append(author)
authors_dicts = [author.__dict__ for author in authors]
return Response(json.dumps(authors_dicts), mimetype="application/json")
和
# book_api.json
import json
from flask import Blueprint, Response
from domain.book import Book
book = Blueprint("book", __name__, url_prefix="/books")
@book.get("/")
def books():
books: list[Book] = []
for i in range(10):
book: Book = Book(str(i), "Book " + str(i))
books.append(book)
books_dicts = [book.__dict__ for book in books]
return Response(json.dumps(books_dicts), mimetype="application/json")
最后我只是在Flask app下注册了两个蓝图
# app.py
from flask import Flask
from api.author.author_api import author
from api.book.book_api import book
app = Flask(__name__)
app.register_blueprint(author, url_prefix="/authors")
app.register_blueprint(book, url_prefix="/books")
@app.get('/')
def hello_world():
return 'Flask - OpenAPI'
if __name__ == '__main__':
app.run()
完整的源代码也可以在 GitHub 上找到。
考虑到这个最小的工作示例,我想知道自动生成 OpenAPI v3 yaml/JSON 文件的最快方法是什么,例如暴露在 /api-doc.yaml 端点上。
PS:这是我第一次 API 使用 Python 和 Flask。我正在尝试重现我可以用 Spring-Boot and SpringDoc
做的事情我鼓励您将您的项目切换到 FastAPI,它与 Flask 没有太大区别或更难。
FastAPI docs about generating OpenAPI schema
它不仅可以让您轻松生成 OpenAPI 文档/规范。它也是异步的,速度更快,更现代。
另请参阅 FastAPI Alternatives, Inspiration and Comparisons 以了解差异。
尤其是上面 link 的引用应该可以解释为什么做你尝试做的事情可能不是最好的主意:
Flask REST frameworks
There are several Flask REST frameworks, but after investing the time and work into investigating them, I found that many are discontinued or abandoned, with several standing issues that made them unfit.
按照从 Flask 迁移到 FastAPI 的建议,我试了一下并重写了 Flask-Example of the question. The source code is also available on GitHub.
项目的结构几乎相同,但有一些附加功能可用(例如 CORS 中间件):
域的模型略有不同并扩展了 BaseModel from Pydantic。
# author.py
from pydantic import BaseModel
class Author(BaseModel):
id: str
name: str
和
# book.py
from pydantic import BaseModel
class Book(BaseModel):
id: str
name: str
FastAPI 等同于 Flask Blueprint 是 APIRouter。 下面是作者的两个控制器
# author_api.py
from fastapi import APIRouter
from domain.author import Author
router = APIRouter()
@router.get("/", tags=["Authors"], response_model=list[Author])
def get_authors() -> list[Author]:
authors: list[Author] = []
for i in range(10):
authors.append(Author(id="Author-" + str(i), name="Author-Name-" + str(i)))
return authors
和书籍
# book_api.py
from fastapi import APIRouter
from domain.book import Book
router = APIRouter()
@router.get("/", tags=["Books"], response_model=list[Book])
def get_books() -> list[Book]:
books: list[Book] = []
for i in range(10):
books.append(Book(id="Book-" + str(i), name="Book-Name-" + str(i)))
return books
重要的是要注意 API 端点的响应模型是使用 Python 类型定义的,这要归功于 Pydantic。然后将这些对象类型转换为 OpenAPI 文档的 JSON 模式。
最后我简单地registered/included FastAPI 对象下的APIRouters 并添加了CORS 的配置。
# app.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from domain.info import Info
from api.author.author_api import router as authors_router
from api.book.book_api import router as books_router
app = FastAPI()
app.include_router(authors_router, prefix="/authors")
app.include_router(books_router, prefix="/books")
app.add_middleware(CORSMiddleware,
allow_credentials=True,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/", response_model=Info)
def info() -> Info:
info = Info(info="FastAPI - OpenAPI")
return info
生成的 OpenAPI 文档可在端点 /openapi.json
访问,而 UI(又名 Swagger UI、Redoc)可在 /docs
访问
和/redoc
综上所述,这是自动生成的 JSON 格式的 OpenAPI v3 文档,可用于轻松生成其他语言的 API 客户端(例如使用OpenAPI-Generator tools).
{
"openapi": "3.0.2",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/authors/": {
"get": {
"tags": [
"Authors"
],
"summary": "Get Authors",
"operationId": "get_authors_authors__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response Get Authors Authors Get",
"type": "array",
"items": {
"$ref": "#/components/schemas/Author"
}
}
}
}
}
}
}
},
"/books/": {
"get": {
"tags": [
"Books"
],
"summary": "Get Books",
"operationId": "get_books_books__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response Get Books Books Get",
"type": "array",
"items": {
"$ref": "#/components/schemas/Book"
}
}
}
}
}
}
}
},
"/": {
"get": {
"summary": "Info",
"operationId": "info__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Info"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Author": {
"title": "Author",
"required": [
"id",
"name"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
}
}
},
"Book": {
"title": "Book",
"required": [
"id",
"name"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
}
}
},
"Info": {
"title": "Info",
"required": [
"info"
],
"type": "object",
"properties": {
"info": {
"title": "Info",
"type": "string"
}
}
}
}
}
}
为了启动应用程序,我们还需要一个用于生产的 ASGI 服务器,例如 Uvicorn or Hypercorn。 我使用 Uvicorn 并使用以下命令启动应用程序:
uvicorn app:app --reload
然后可以在您机器的 8000 端口上使用。
如果您想坚持使用 Flask,swagger-gen
是一个可以生成 full-featured 规范且实现开销非常低的库。
from swagger_gen.lib.wrappers import swagger_metadata
from swagger_gen.lib.security import BearerAuth
from swagger_gen.swagger import Swagger
from flask import Flask, request
app = Flask(__name__)
@app.route('/api/hello/say', methods=['GET'])
@swagger_metadata(
summary='Sample endpoint',
description='This is a sample endpoint')
def test():
return {'message': 'hello world!'}
swagger = Swagger(
app=app,
title='app')
swagger.configure()
if __name__ == '__main__':
app.run(debug=True, port='5000')
完全披露:我是作者。