你可以修改FastAPI的get_swagger_ui_html() https://github.com/tiangolo/fastapi/blob/1574c9623103d03d27117f64b937d754cba12584/fastapi/openapi/docs.py#L16函数,以注入一些自定义 JavaScript 代码,如 @lunaa 所描述here https://stackoverflow.com/a/75022153/17865804,并通过以下方式以编程方式创建自定义 HTML 按钮custom_script.js
。然而,自从Authorize
按钮元素是在 DOM/Window 加载后创建的 - 并且似乎没有本地方法可以在定义后运行 JS 代码,即使您使用Window.load https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event事件来运行 JavaScript 代码 - 并且您需要在其旁边添加自定义按钮,您可以使用所描述的方法简单地等待该元素被创建here https://stackoverflow.com/a/61511955,然后创建自定义按钮并将其添加到 DOM。
完整的工作示例
app.py
from fastapi import FastAPI
from fastapi import Depends
from fastapi.security import OpenIdConnect
from fastapi.staticfiles import StaticFiles
from fastapi.openapi.docs import (
get_redoc_html,
get_swagger_ui_oauth2_redirect_html,
)
from custom_swagger import get_swagger_ui_html
app = FastAPI(docs_url=None)
app.mount("/static", StaticFiles(directory="static"), name="static")
oidc_google = OpenIdConnect(openIdConnectUrl='https://accounts.google.com/.well-known/openid-configuration')
@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
return get_swagger_ui_html(
openapi_url=app.openapi_url,
title="My API",
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
#swagger_js_url="/static/swagger-ui-bundle.js", # Optional
#swagger_css_url="/static/swagger-ui.css", # Optional
#swagger_favicon_url="/static/favicon-32x32.png", # Optional
custom_js_url="/static/custom_script.js",
)
@app.get('/')
def main(token: str = Depends(oidc_google)):
return "You are Authenticated"
自定义_swagger.py
import json
from typing import Any, Dict, Optional
from fastapi.encoders import jsonable_encoder
from fastapi.openapi.docs import swagger_ui_default_parameters
from starlette.responses import HTMLResponse
def get_swagger_ui_html(
*,
openapi_url: str,
title: str,
swagger_js_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui-bundle.js",
swagger_css_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui.css",
swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
oauth2_redirect_url: Optional[str] = None,
init_oauth: Optional[Dict[str, Any]] = None,
swagger_ui_parameters: Optional[Dict[str, Any]] = None,
custom_js_url: Optional[str] = None,
) -> HTMLResponse:
current_swagger_ui_parameters = swagger_ui_default_parameters.copy()
if swagger_ui_parameters:
current_swagger_ui_parameters.update(swagger_ui_parameters)
html = f"""
<!DOCTYPE html>
<html>
<head>
<link type="text/css" rel="stylesheet" href="{swagger_css_url}">
<link rel="shortcut icon" href="{swagger_favicon_url}">
<title>{title}</title>
</head>
<body>
<div id="swagger-ui">
</div>
"""
if custom_js_url:
html += f"""
<script src="{custom_js_url}"></script>
"""
html += f"""
<script src="{swagger_js_url}"></script>
<!-- `SwaggerUIBundle` is now available on the page -->
<script>
const ui = SwaggerUIBundle({{
url: '{openapi_url}',
"""
for key, value in current_swagger_ui_parameters.items():
html += f"{json.dumps(key)}: {json.dumps(jsonable_encoder(value))},\n"
if oauth2_redirect_url:
html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}',"
html += """
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
})"""
if init_oauth:
html += f"""
ui.initOAuth({json.dumps(jsonable_encoder(init_oauth))})
"""
html += """
</script>
</body>
</html>
"""
return HTMLResponse(html)
static/自定义脚本.js
function waitForElm(selector) {
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
resolve(document.querySelector(selector));
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
waitForElm('.auth-wrapper').then((elm) => {
var authWrapper = document.getElementsByClassName("auth-wrapper")[0];
var btn = document.createElement("BUTTON");
btn.innerHTML = "Click me";
btn.id = "btn-id";
btn.onclick = function() {
alert("button is clicked");
};
authWrapper.append(btn);
});
不是通过 JavaScript 以编程方式创建按钮,您可以加载外部 HTML 文件(使用 JavaScript),其中包含按钮的 HTML 代码以及您可能想要插入的任何其他元素。下面的例子:
static/自定义脚本.js
function waitForElm(selector) {
// same as in the previous code snippet
}
waitForElm('.auth-wrapper').then((elm) => {
var authWrapper = document.getElementsByClassName("auth-wrapper")[0];
fetch('/static/button.html')
.then(response => response.text())
.then(text => {
const newDiv = document.createElement("div");
newDiv.innerHTML = text;
authWrapper.append(newDiv);
});
});
static/按钮.html
<button onclick="alert('button is clicked');" class="btn authorize unlocked Google">
<span>Authorize Google</span>
<svg width="20" height="20">
<use href="#unlocked" xlink:href="#unlocked"></use>
</svg>
</button>
添加动态自定义内容
如果您想添加一些动态内容,而不是静态 JS/HTML 文件内容,您可以将内容直接作为字符串传递给get_swagger_ui_html()
函数,或者使用静态内容与动态变量的组合,可以使用 Jinja2 模板添加动态变量。下面给出了示例,演示了对前面提供的代码进行的更改 - 其余代码应与上面相同。下面的例子中的动态变量是msg
.
Example
app.py
# ...
from jinja2 import Environment, FileSystemLoader
def get_template():
env = Environment(loader=FileSystemLoader('./static'))
template = env.get_template('custom_script.js')
context = {'msg': 'button is clicked!'}
html = template.render(context)
return html
@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
return get_swagger_ui_html(
openapi_url=app.openapi_url,
title="My API",
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
custom_js_content=get_template()
)
自定义_swagger.py
def get_swagger_ui_html(
*,
# ...
custom_js_content: Optional[str] = None,
) -> HTMLResponse:
# ...
if custom_js_content:
html += f"""
<script>{custom_js_content}</script>
"""
# ...
static/自定义脚本.js
function waitForElm(selector) {
// ...
}
waitForElm('.auth-wrapper').then((elm) => {
var authWrapper = document.getElementsByClassName("auth-wrapper")[0];
var btn = document.createElement("BUTTON");
btn.innerHTML = `
<span>Authorize Google</span>
<svg width="20" height="20">
<use href="#unlocked" xlink:href="#unlocked"></use>
</svg>
`;
btn.className = "btn authorize unlocked Google";
btn.onclick = function() {
alert("{{msg}}");
};
authWrapper.append(btn);
});
or
static/自定义脚本.js
function waitForElm(selector) {
// ...
}
waitForElm('.auth-wrapper').then((elm) => {
var authWrapper = document.getElementsByClassName("auth-wrapper")[0];
var html = `
<button onclick="alert('{{msg}}');" class="btn authorize unlocked Google">
<span>Authorize Google</span>
<svg width="20" height="20">
<use href="#unlocked" xlink:href="#unlocked"></use>
</svg>
</button>
`;
var newDiv = document.createElement("div");
newDiv.innerHTML = html;
authWrapper.append(newDiv);
});