FastAPI是用于构建API的现代Python网络框架。 FastAPI是构建简单API的绝佳选择,并带有内置支持以生成OpenAPI文档。
在这篇文章中,我们将研究如何从Fastapi项目中生成和提取OpenAPI specification。
在我们的测试台上,从the official docs取的一个简单的单文件FastApi示例就足够了。相同的工作流也适用于路由器的大型项目。
步骤1:添加标签和元数据
可能会使用OpenAPI规格生成文档或代码。重要的是要在您的FastApi应用中添加元数据,以使生成的OpenAPI规格完成。
首先,您应该使用tags
标记我们的端点,以确保它们将其分组为逻辑操作。此示例不使用routers,但是如果您这样做,则需要标记路由器而不是端点。
标签由文档和代码生成器使用将端点分组在一起。标签可能包括空间和特殊字符,但我们建议将标签保持简单。通常将小写或资本案例用于标签,例如我们的示例中的Items
。
除了标签外,我们还将在我们的FastApi App实例中添加description
和version
元数据。 description
和version
将在概述页面的生成的OpenAPI文档中使用。如果需要在规范中包含其他详细信息,请在Fastapi文档中找到metadata parameters的完整列表。
现在看起来像这样的完整示例main.py
:
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(description="Example app", version="0.1.0")
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
@app.post("/items/", tags=["Items"])
async def create_item(item: Item) -> Item:
return item
@app.get("/items/", tags=["Items"])
async def read_items() -> list[Item]:
return [
Item(name="Portal Gun", price=42.0),
Item(name="Plumbus", price=32.0),
]
此示例需要pip install fastapi[all]
和pip install pydantic
运行。
步骤2:创建导出脚本
默认情况下,FastApi将在/docs
下生成OpenAPI文档。您可以通过运行应用程序并导航到http://localhost:8000/docs
尝试一下。
可以通过导航到/openapi.json
直接获得OpenAPI JSON,但是我们希望以编程方式提取文档以能够自动化该过程。 FastAPI不支持直接导出OpenAPI规范,但我们将使用一个小脚本提取它。
创建一个带有以下内容的文件extract-openapi.py
:
# extract-openapi.py
import argparse
import json
import sys
import yaml
from uvicorn.importer import import_from_string
parser = argparse.ArgumentParser(prog="extract-openapi.py")
parser.add_argument("app", help='App import string. Eg. "main:app"', default="main:app")
parser.add_argument("--app-dir", help="Directory containing the app", default=None)
parser.add_argument("--out", help="Output file ending in .json or .yaml", default="openapi.yaml")
if __name__ == "__main__":
args = parser.parse_args()
if args.app_dir is not None:
print(f"adding {args.app_dir} to sys.path")
sys.path.insert(0, args.app_dir)
print(f"importing app from {args.app}")
app = import_from_string(args.app)
openapi = app.openapi()
version = openapi.get("openapi", "unknown version")
print(f"writing openapi spec v{version}")
with open(args.out, "w") as f:
if args.out.endswith(".json"):
json.dump(openapi, f, indent=2)
else:
yaml.dump(openapi, f, sort_keys=False)
print(f"spec written to {args.out}")
此脚本所做的是从给定的导入字符串导入应用程序,然后调用app.openapi()
获取OpenAPI Spec。然后将规格写入给定的输出文件。
您可以调用帮助查看可用选项:
$ python3 export-openapi.py --help
usage: extract-openapi.py [-h] [--app-dir APP_DIR] [--out OUT] app
positional arguments:
app App import string. Eg. "main:app"
options:
-h, --help show this help message and exit
--app-dir APP_DIR Directory containing the app
--out OUT Output file ending in .json or .yaml
如果您在uvicorn
导入方面遇到麻烦,请确保已安装了包括uvicorn在内的fastapi[all]
软件包,或者安装了uvicorn
。
步骤3:从Fastapi导出OpenAPI规格
运行脚本
要运行export script,您需要知道FastApi应用程序的导入字符串。 导入字符串是您运行应用程序时向Uvicorn传递的内容,例如uvicorn main:app
。
这取决于您的代码的位置以及fastapi实例的名称。有关如何确定导入字符串的提示,请参见下文。
# Run the script by passing the import string of your FastAPI app
# If you don't know the import string, see below for examples
$ python extract-openapi.py main:app
这应该在您当前目录中创建openapi.json
或openapi.yaml
文件。
示例案例:简单的项目结构
在我们的示例中,我们有一个类似的项目结构:
/my_project
├── extract-openapi.py
└── main.py
我们的fastapi实例是app
,因为我们在main.py
中编写了app = FastAPI()
。我们在当前目录中也有一个main.py
,因此导入字符串为main:app
。
提取我们做的openapi规格
$ python extract-openapi.py main:app
示例案例:嵌套项目结构
对于较大的应用程序,项目结构通常更嵌套,例如:
/my_project
├── extract-openapi.py
└── myapp
└── main.py
在这种情况下,我们必须将模块名称myapp
添加到导入字符串中。导入字符串现在为myapp.main:app
。另外,如果您对此遇到麻烦,则可以使用--app-dir
参数指定包含应用程序的入口点的目录。
在这种情况下,要提取OpenAPI规格,我们要做
$ python extract-openapi.py myapp.main:app
# or alternatively
$ python extract-openapi.py --app-dir myapp main:app
您现在应该在当前目录中有openapi.json
或openapi.yaml
文件。
步骤4:在您的CI/CD管道中自动化(可选)
您将如何将提取到CI/CD集成取决于您要完成的工作。接近这一点的三种最常见方法是:
- 在本地提取规格并将其提交给您的存储库。令CI/CD验证所承诺的规格是最新的。
- 将规格作为CI/CD管道的一部分提取,并将规格用作临时文件来完成某些事情(例如,生成客户端)。
- 将规格作为CI/CD管道的一部分提取,并在合并到Main时将生成的规格提交给您的存储库。
使用脚本的好处是它也可以在本地运行。本地承诺通常是一种安全且直接的方法,但有时可能会使合并更加困难。但是,如果您只需要作为CI/CD管道的一部分生成OpenAPI规格,则还应考虑专用的GitHub操作。
作为一个例子,我们将演示一个github动作作业,该作业验证了订婚规格与生成的规格匹配:
# .github/workflows/main.yml
name: CI
on: [push]
jobs:
extract-openapi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.11
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Extract OpenAPI spec
run: python extract-openapi.py main:app --out openapi_generated.yaml
# Do something with the generated spec here.
# For example, validate that the committed spec matches the generated one.
- name: Verify OpenAPI spec has been updated
run: git diff --exit-code openapi.yaml openapi_generated.yaml
概括
总而言之,我们有
- 将
tags
添加到每个端点或路由器 - 在我们的fastapi应用程序实例 中添加了
- 创建一个脚本
extract-openapi.py
,以从FastApi提取OpenAPI Spec - 在CI/CD管道中自动提取
description
,version
和其他元数据