fastapi/openapi/docs.py PYTHON 390 lines View on github.com → Search inside
1import json2from typing import Annotated, Any34from annotated_doc import Doc5from fastapi.encoders import jsonable_encoder6from starlette.responses import HTMLResponse789def _html_safe_json(value: Any) -> str:10    """Serialize a value to JSON with HTML special characters escaped.1112    This prevents injection when the JSON is embedded inside a <script> tag.13    """14    return (15        json.dumps(value)16        .replace("<", "\\u003c")17        .replace(">", "\\u003e")18        .replace("&", "\\u0026")19    )202122swagger_ui_default_parameters: Annotated[23    dict[str, Any],24    Doc(25        """26        Default configurations for Swagger UI.2728        You can use it as a template to add any other configurations needed.29        """30    ),31] = {32    "dom_id": "#swagger-ui",33    "layout": "BaseLayout",34    "deepLinking": True,35    "showExtensions": True,36    "showCommonExtensions": True,37}383940def get_swagger_ui_html(41    *,42    openapi_url: Annotated[43        str,44        Doc(45            """46            The OpenAPI URL that Swagger UI should load and use.4748            This is normally done automatically by FastAPI using the default URL49            `/openapi.json`.5051            Read more about it in the52            [FastAPI docs for Conditional OpenAPI](https://fastapi.tiangolo.com/how-to/conditional-openapi/#conditional-openapi-from-settings-and-env-vars)53            """54        ),55    ],56    title: Annotated[57        str,58        Doc(59            """60            The HTML `<title>` content, normally shown in the browser tab.6162            Read more about it in the63            [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)64            """65        ),66    ],67    swagger_js_url: Annotated[68        str,69        Doc(70            """71            The URL to use to load the Swagger UI JavaScript.7273            It is normally set to a CDN URL.7475            Read more about it in the76            [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)77            """78        ),79    ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",80    swagger_css_url: Annotated[81        str,82        Doc(83            """84            The URL to use to load the Swagger UI CSS.8586            It is normally set to a CDN URL.8788            Read more about it in the89            [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)90            """91        ),92    ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",93    swagger_favicon_url: Annotated[94        str,95        Doc(96            """97            The URL of the favicon to use. It is normally shown in the browser tab.98            """99        ),100    ] = "https://fastapi.tiangolo.com/img/favicon.png",101    oauth2_redirect_url: Annotated[102        str | None,103        Doc(104            """105            The OAuth2 redirect URL, it is normally automatically handled by FastAPI.106107            Read more about it in the108            [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)109            """110        ),111    ] = None,112    init_oauth: Annotated[113        dict[str, Any] | None,114        Doc(115            """116            A dictionary with Swagger UI OAuth2 initialization configurations.117118            Read more about the available configuration options in the119            [Swagger UI docs](https://swagger.io/docs/open-source-tools/swagger-ui/usage/oauth2/).120            """121        ),122    ] = None,123    swagger_ui_parameters: Annotated[124        dict[str, Any] | None,125        Doc(126            """127            Configuration parameters for Swagger UI.128129            It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters].130131            Read more about it in the132            [FastAPI docs about how to Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/).133            """134        ),135    ] = None,136) -> HTMLResponse:137    """138    Generate and return the HTML  that loads Swagger UI for the interactive139    API docs (normally served at `/docs`).140141    You would only call this function yourself if you needed to override some parts,142    for example the URLs to use to load Swagger UI's JavaScript and CSS.143144    Read more about it in the145    [FastAPI docs for Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/)146    and the [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).147    """148    current_swagger_ui_parameters = swagger_ui_default_parameters.copy()149    if swagger_ui_parameters:150        current_swagger_ui_parameters.update(swagger_ui_parameters)151152    html = f"""153    <!DOCTYPE html>154    <html>155    <head>156    <meta name="viewport" content="width=device-width, initial-scale=1.0">157    <link type="text/css" rel="stylesheet" href="{swagger_css_url}">158    <link rel="shortcut icon" href="{swagger_favicon_url}">159    <title>{title}</title>160    </head>161    <body>162    <div id="swagger-ui">163    </div>164    <script src="{swagger_js_url}"></script>165    <!-- `SwaggerUIBundle` is now available on the page -->166    <script>167    const ui = SwaggerUIBundle({{168        url: '{openapi_url}',169    """170171    for key, value in current_swagger_ui_parameters.items():172        html += f"{_html_safe_json(key)}: {_html_safe_json(jsonable_encoder(value))},\n"173174    if oauth2_redirect_url:175        html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}',"176177    html += """178    presets: [179        SwaggerUIBundle.presets.apis,180        SwaggerUIBundle.SwaggerUIStandalonePreset181        ],182    })"""183184    if init_oauth:185        html += f"""186        ui.initOAuth({_html_safe_json(jsonable_encoder(init_oauth))})187        """188189    html += """190    </script>191    </body>192    </html>193    """194    return HTMLResponse(html)195196197def get_redoc_html(198    *,199    openapi_url: Annotated[200        str,201        Doc(202            """203            The OpenAPI URL that ReDoc should load and use.204205            This is normally done automatically by FastAPI using the default URL206            `/openapi.json`.207208            Read more about it in the209            [FastAPI docs for Conditional OpenAPI](https://fastapi.tiangolo.com/how-to/conditional-openapi/#conditional-openapi-from-settings-and-env-vars)210            """211        ),212    ],213    title: Annotated[214        str,215        Doc(216            """217            The HTML `<title>` content, normally shown in the browser tab.218219            Read more about it in the220            [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)221            """222        ),223    ],224    redoc_js_url: Annotated[225        str,226        Doc(227            """228            The URL to use to load the ReDoc JavaScript.229230            It is normally set to a CDN URL.231232            Read more about it in the233            [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)234            """235        ),236    ] = "https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js",237    redoc_favicon_url: Annotated[238        str,239        Doc(240            """241            The URL of the favicon to use. It is normally shown in the browser tab.242            """243        ),244    ] = "https://fastapi.tiangolo.com/img/favicon.png",245    with_google_fonts: Annotated[246        bool,247        Doc(248            """249            Load and use Google Fonts.250            """251        ),252    ] = True,253) -> HTMLResponse:254    """255    Generate and return the HTML response that loads ReDoc for the alternative256    API docs (normally served at `/redoc`).257258    You would only call this function yourself if you needed to override some parts,259    for example the URLs to use to load ReDoc's JavaScript and CSS.260261    Read more about it in the262    [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).263    """264    html = f"""265    <!DOCTYPE html>266    <html>267    <head>268    <title>{title}</title>269    <!-- needed for adaptive design -->270    <meta charset="utf-8"/>271    <meta name="viewport" content="width=device-width, initial-scale=1">272    """273    if with_google_fonts:274        html += """275    <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">276    """277    html += f"""278    <link rel="shortcut icon" href="{redoc_favicon_url}">279    <!--280    ReDoc doesn't change outer page styles281    -->282    <style>283      body {{284        margin: 0;285        padding: 0;286      }}287    </style>288    </head>289    <body>290    <noscript>291        ReDoc requires Javascript to function. Please enable it to browse the documentation.292    </noscript>293    <redoc spec-url="{openapi_url}"></redoc>294    <script src="{redoc_js_url}"> </script>295    </body>296    </html>297    """298    return HTMLResponse(html)299300301def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse:302    """303    Generate the HTML response with the OAuth2 redirection for Swagger UI.304305    You normally don't need to use or change this.306    """307    # copied from https://github.com/swagger-api/swagger-ui/blob/v4.14.0/dist/oauth2-redirect.html308    html = """309    <!doctype html>310    <html lang="en-US">311    <head>312        <title>Swagger UI: OAuth2 Redirect</title>313    </head>314    <body>315    <script>316        'use strict';317        function run () {318            var oauth2 = window.opener.swaggerUIRedirectOauth2;319            var sentState = oauth2.state;320            var redirectUrl = oauth2.redirectUrl;321            var isValid, qp, arr;322323            if (/code|token|error/.test(window.location.hash)) {324                qp = window.location.hash.substring(1).replace('?', '&');325            } else {326                qp = location.search.substring(1);327            }328329            arr = qp.split("&");330            arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});331            qp = qp ? JSON.parse('{' + arr.join() + '}',332                    function (key, value) {333                        return key === "" ? value : decodeURIComponent(value);334                    }335            ) : {};336337            isValid = qp.state === sentState;338339            if ((340              oauth2.auth.schema.get("flow") === "accessCode" ||341              oauth2.auth.schema.get("flow") === "authorizationCode" ||342              oauth2.auth.schema.get("flow") === "authorization_code"343            ) && !oauth2.auth.code) {344                if (!isValid) {345                    oauth2.errCb({346                        authId: oauth2.auth.name,347                        source: "auth",348                        level: "warning",349                        message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."350                    });351                }352353                if (qp.code) {354                    delete oauth2.state;355                    oauth2.auth.code = qp.code;356                    oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});357                } else {358                    let oauthErrorMsg;359                    if (qp.error) {360                        oauthErrorMsg = "["+qp.error+"]: " +361                            (qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +362                            (qp.error_uri ? "More info: "+qp.error_uri : "");363                    }364365                    oauth2.errCb({366                        authId: oauth2.auth.name,367                        source: "auth",368                        level: "error",369                        message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."370                    });371                }372            } else {373                oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});374            }375            window.close();376        }377378        if (document.readyState !== 'loading') {379            run();380        } else {381            document.addEventListener('DOMContentLoaded', function () {382                run();383            });384        }385    </script>386    </body>387    </html>388        """389    return HTMLResponse(content=html)

Code quality findings 2

Ensure functions have docstrings for documentation
missing-docstring
def get_swagger_ui_html(
Ensure functions have docstrings for documentation
missing-docstring
def get_redoc_html(

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.