跳转至

扩展 OpenAPI

警告

本章介绍的功能较难,您可以跳过阅读。

如果您刚开始学习用户指南,最好跳过本章。

如果您确定要修改 OpenAPI 概图,请继续阅读。

某些情况下,我们需要修改 OpenAPI 概图。

本章介绍如何修改 OpenAPI 概图。

常规流程

常规(默认)流程如下。

FastAPI 应用(实例)提供了返回 OpenAPI 概图的 .openapi() 方法。

作为应用对象创建的组成部分,要注册 /openapi.json (或其他为 openapi_url 设置的任意内容)路径操作

它只返回包含应用的 .openapi() 方法操作结果的 JSON 响应。

但默认情况下,.openapi() 只是检查 .openapi_schema 属性是否包含内容,并返回其中的内容。

如果 .openapi_schema 属性没有内容,该方法就使用 fastapi.openapi.utils.get_openapi 工具函数生成内容。

get_openapi() 函数接收如下参数:

  • title:文档中显示的 OpenAPI 标题
  • version:API 的版本号,例如 2.5.0
  • openapi_version: OpenAPI 规范的版本号,默认为最新版: 3.0.2
  • description:API 的描述说明
  • routes:路由列表,每个路由都是注册的路径操作。这些路由是从 app.routes 中提取的。

覆盖默认值

get_openapi() 工具函数还可以用于生成 OpenAPI 概图,并利用上述信息参数覆盖指定的内容。

例如,使用 ReDoc 的 OpenAPI 扩展添加自定义 Logo

常规 FastAPI

首先,编写常规 FastAPI 应用:

from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

app = FastAPI()


@app.get("/items/")
async def read_items():
    return [{"name": "Foo"}]


def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = get_openapi(
        title="Custom title",
        version="2.5.0",
        description="This is a very custom OpenAPI schema",
        routes=app.routes,
    )
    openapi_schema["info"]["x-logo"] = {
        "url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
    }
    app.openapi_schema = openapi_schema
    return app.openapi_schema


app.openapi = custom_openapi

生成 OpenAPI 概图

然后,在 custom_openapi() 函数里使用 get_openapi() 工具函数生成 OpenAPI 概图:

from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

app = FastAPI()


@app.get("/items/")
async def read_items():
    return [{"name": "Foo"}]


def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = get_openapi(
        title="Custom title",
        version="2.5.0",
        description="This is a very custom OpenAPI schema",
        routes=app.routes,
    )
    openapi_schema["info"]["x-logo"] = {
        "url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
    }
    app.openapi_schema = openapi_schema
    return app.openapi_schema


app.openapi = custom_openapi

修改 OpenAPI 概图

添加 ReDoc 扩展信息,为 OpenAPI 概图里的 info 对象添加自定义 x-logo

from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

app = FastAPI()


@app.get("/items/")
async def read_items():
    return [{"name": "Foo"}]


def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = get_openapi(
        title="Custom title",
        version="2.5.0",
        description="This is a very custom OpenAPI schema",
        routes=app.routes,
    )
    openapi_schema["info"]["x-logo"] = {
        "url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
    }
    app.openapi_schema = openapi_schema
    return app.openapi_schema


app.openapi = custom_openapi

缓存 OpenAPI 概图

.openapi_schema 属性当作缓存,存储生成的概图。

通过这种方式,FastAPI 应用不必在用户每次打开 API 文档时反复生成概图。

只需生成一次,下次请求时就可以使用缓存的概图。

from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

app = FastAPI()


@app.get("/items/")
async def read_items():
    return [{"name": "Foo"}]


def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = get_openapi(
        title="Custom title",
        version="2.5.0",
        description="This is a very custom OpenAPI schema",
        routes=app.routes,
    )
    openapi_schema["info"]["x-logo"] = {
        "url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
    }
    app.openapi_schema = openapi_schema
    return app.openapi_schema


app.openapi = custom_openapi

覆盖方法

用新函数替换 .openapi() 方法。

from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

app = FastAPI()


@app.get("/items/")
async def read_items():
    return [{"name": "Foo"}]


def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = get_openapi(
        title="Custom title",
        version="2.5.0",
        description="This is a very custom OpenAPI schema",
        routes=app.routes,
    )
    openapi_schema["info"]["x-logo"] = {
        "url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
    }
    app.openapi_schema = openapi_schema
    return app.openapi_schema


app.openapi = custom_openapi

查看文档

打开 http://127.0.0.1:8000/redoc,查看自定义 Logo(本例中是 FastAPI 的 Logo):

文档 JavaScript 与 CSS 自托管

FastAPI 内置了 Swagger UIReDoc 文档,这两种文档都需要调用 JavaScript 与 CSS 文件。

这些文件默认由 CDN 提供支持服务。

但也可以自定义设置指定的 CDN 或自行提供文件服务。

这种做法很常用,例如,在没有联网或本地局域网时也能让应用在离线状态下正常运行。

本文介绍如何为 FastAPI 应用提供文件自托管服务,并设置文档使用这些文件。

项目文件架构

假设项目文件架构如下:

.
├── app
│   ├── __init__.py
│   ├── main.py

接下来,创建存储静态文件的文件夹。

新的文件架构如下:

.
├── app
│   ├── __init__.py
│   ├── main.py
└── static/

下载文件

下载文档所需的静态文件,把文件放到 static/ 文件夹里。

右键点击链接,选择另存为...

Swagger UI 使用如下文件:

ReDoc 使用如下文件:

保存好后,文件架构所示如下:

.
├── app
│   ├── __init__.py
│   ├── main.py
└── static
    ├── redoc.standalone.js
    ├── swagger-ui-bundle.js
    └── swagger-ui.css

安装 aiofiles

现在,安装 aiofiles

$ pip install aiofiles

---> 100%

静态文件服务

  • 导入 StaticFiles
  • 在指定路径下挂载 StaticFiles() 实例
from fastapi import FastAPI
from fastapi.openapi.docs import (
    get_redoc_html,
    get_swagger_ui_html,
    get_swagger_ui_oauth2_redirect_html,
)
from fastapi.staticfiles import StaticFiles

app = FastAPI(docs_url=None, redoc_url=None)

app.mount("/static", StaticFiles(directory="static"), name="static")


@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
    return get_swagger_ui_html(
        openapi_url=app.openapi_url,
        title=app.title + " - Swagger UI",
        oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
        swagger_js_url="/static/swagger-ui-bundle.js",
        swagger_css_url="/static/swagger-ui.css",
    )


@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
async def swagger_ui_redirect():
    return get_swagger_ui_oauth2_redirect_html()


@app.get("/redoc", include_in_schema=False)
async def redoc_html():
    return get_redoc_html(
        openapi_url=app.openapi_url,
        title=app.title + " - ReDoc",
        redoc_js_url="/static/redoc.standalone.js",
    )


@app.get("/users/{username}")
async def read_user(username: str):
    return {"message": f"Hello {username}"}

测试静态文件

启动应用,打开 http://127.0.0.1:8000/static/redoc.standalone.js。

就能看到 ReDoc 的 JavaScript 文件。

该文件开头如下:

/*!
 * ReDoc - OpenAPI/Swagger-generated API Reference Documentation
 * -------------------------------------------------------------
 *   Version: "2.0.0-rc.18"
 *   Repo: https://github.com/Redocly/redoc
 */
!function(e,t){"object"==typeof exports&&"object"==typeof m

...

能打开这个文件就表示 FastAPI 应用能提供静态文件服务,并且文档要调用的静态文件放到了正确的位置。

接下来,使用静态文件配置文档。

禁用 API 文档

第一步是禁用 API 文档,就是使用 CDN 的默认文档。

创建 FastAPI 应用时把文档的 URL 设置为 None 即可禁用默认文档:

from fastapi import FastAPI
from fastapi.openapi.docs import (
    get_redoc_html,
    get_swagger_ui_html,
    get_swagger_ui_oauth2_redirect_html,
)
from fastapi.staticfiles import StaticFiles

app = FastAPI(docs_url=None, redoc_url=None)

app.mount("/static", StaticFiles(directory="static"), name="static")


@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
    return get_swagger_ui_html(
        openapi_url=app.openapi_url,
        title=app.title + " - Swagger UI",
        oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
        swagger_js_url="/static/swagger-ui-bundle.js",
        swagger_css_url="/static/swagger-ui.css",
    )


@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
async def swagger_ui_redirect():
    return get_swagger_ui_oauth2_redirect_html()


@app.get("/redoc", include_in_schema=False)
async def redoc_html():
    return get_redoc_html(
        openapi_url=app.openapi_url,
        title=app.title + " - ReDoc",
        redoc_js_url="/static/redoc.standalone.js",
    )


@app.get("/users/{username}")
async def read_user(username: str):
    return {"message": f"Hello {username}"}

添加自定义文档

现在,创建自定义文档的路径操作

导入 FastAPI 内部函数为文档创建 HTML 页面,并把所需参数传递给这些函数:

  • openapi_url: API 文档获取 OpenAPI 概图的 HTML 页面,此处可使用 app.openapi_url
  • title:API 的标题
  • oauth2_redirect_url:此处使用 app.swagger_ui_oauth2_redirect_url 作为默认值
  • swagger_js_url:Swagger UI 文档所需 JavaScript 文件的 URL,即为应用提供服务的文件
  • swagger_css_url:Swagger UI 文档所需 CSS 文件的 URL,即为应用提供服务的文件

添加 ReDoc 文档的方式与此类似……

from fastapi import FastAPI
from fastapi.openapi.docs import (
    get_redoc_html,
    get_swagger_ui_html,
    get_swagger_ui_oauth2_redirect_html,
)
from fastapi.staticfiles import StaticFiles

app = FastAPI(docs_url=None, redoc_url=None)

app.mount("/static", StaticFiles(directory="static"), name="static")


@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
    return get_swagger_ui_html(
        openapi_url=app.openapi_url,
        title=app.title + " - Swagger UI",
        oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
        swagger_js_url="/static/swagger-ui-bundle.js",
        swagger_css_url="/static/swagger-ui.css",
    )


@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
async def swagger_ui_redirect():
    return get_swagger_ui_oauth2_redirect_html()


@app.get("/redoc", include_in_schema=False)
async def redoc_html():
    return get_redoc_html(
        openapi_url=app.openapi_url,
        title=app.title + " - ReDoc",
        redoc_js_url="/static/redoc.standalone.js",
    )


@app.get("/users/{username}")
async def read_user(username: str):
    return {"message": f"Hello {username}"}

提示

swagger_ui_redirect路径操作是 OAuth2 的辅助函数。

集成 API 与 OAuth2 第三方应用时,您能进行身份验证,使用请求凭证返回 API 文档,并使用真正的 OAuth2 身份验证与 API 文档进行交互操作。

Swagger UI 在后台进行处理,但它需要这个重定向辅助函数。

创建测试路径操作

现在,测试各项功能是否能顺利运行。创建路径操作

from fastapi import FastAPI
from fastapi.openapi.docs import (
    get_redoc_html,
    get_swagger_ui_html,
    get_swagger_ui_oauth2_redirect_html,
)
from fastapi.staticfiles import StaticFiles

app = FastAPI(docs_url=None, redoc_url=None)

app.mount("/static", StaticFiles(directory="static"), name="static")


@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
    return get_swagger_ui_html(
        openapi_url=app.openapi_url,
        title=app.title + " - Swagger UI",
        oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
        swagger_js_url="/static/swagger-ui-bundle.js",
        swagger_css_url="/static/swagger-ui.css",
    )


@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
async def swagger_ui_redirect():
    return get_swagger_ui_oauth2_redirect_html()


@app.get("/redoc", include_in_schema=False)
async def redoc_html():
    return get_redoc_html(
        openapi_url=app.openapi_url,
        title=app.title + " - ReDoc",
        redoc_js_url="/static/redoc.standalone.js",
    )


@app.get("/users/{username}")
async def read_user(username: str):
    return {"message": f"Hello {username}"}

测试文档

断开 WiFi 连接,打开 http://127.0.0.1:8000/docs,刷新页面。

现在,就算没有联网也能查看并操作 API 文档。