Coverage for fastapi / openapi / docs.py: 100%
31 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
1import json 1YZ01
2from typing import Annotated, Any 1YZ01
4from annotated_doc import Doc 1YZ01
5from fastapi.encoders import jsonable_encoder 1YZ01
6from starlette.responses import HTMLResponse 1YZ01
9def _html_safe_json(value: Any) -> str: 1YZ01
10 """Serialize a value to JSON with HTML special characters escaped.
12 This prevents injection when the JSON is embedded inside a <script> tag.
13 """
14 return ( 1pqPQRrstdaefguvwxyzASTUBCDhbijkEFGHIVWXJKLlcmnoMNO
15 json.dumps(value)
16 .replace("<", "\\u003c")
17 .replace(">", "\\u003e")
18 .replace("&", "\\u0026")
19 )
22swagger_ui_default_parameters: Annotated[ 1YZ01
23 dict[str, Any],
24 Doc(
25 """
26 Default configurations for Swagger UI.
28 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}
40def get_swagger_ui_html( 1YZ01
41 *,
42 openapi_url: Annotated[
43 str,
44 Doc(
45 """
46 The OpenAPI URL that Swagger UI should load and use.
48 This is normally done automatically by FastAPI using the default URL
49 `/openapi.json`.
51 Read more about it in the
52 [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.
62 Read more about it in the
63 [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.
73 It is normally set to a CDN URL.
75 Read more about it in the
76 [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.
86 It is normally set to a CDN URL.
88 Read more about it in the
89 [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.
107 Read more about it in the
108 [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.
118 Read more about the available configuration options in the
119 [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.
129 It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters].
131 Read more about it in the
132 [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 interactive
139 API docs (normally served at `/docs`).
141 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.
144 Read more about it in the
145 [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() 1pqPQRrstdaefguvwxyzASTUBCDhbijkEFGHIVWXJKLlcmnoMNO
149 if swagger_ui_parameters: 1pqPQRrstdaefguvwxyzASTUBCDhbijkEFGHIVWXJKLlcmnoMNO
150 current_swagger_ui_parameters.update(swagger_ui_parameters) 1tefg~DijkLmno
152 html = f""" 1pqPQRrstdaefguvwxyzASTUBCDhbijkEFGHIVWXJKLlcmnoMNO
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 """
171 for key, value in current_swagger_ui_parameters.items(): 1pqPQRrstdaefguvwxyzASTUBCDhbijkEFGHIVWXJKLlcmnoMNO
172 html += f"{_html_safe_json(key)}: {_html_safe_json(jsonable_encoder(value))},\n" 1pqPQRrstdaefguvwxyzASTUBCDhbijkEFGHIVWXJKLlcmnoMNO
174 if oauth2_redirect_url: 1pqPQRrstdaefguvwxyzASTUBCDhbijkEFGHIVWXJKLlcmnoMNO
175 html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}'," 1pqdaefguvwxyzAhbijkEFGHIlcmnoMNO
177 html += """ 1pqPQRrstdaefguvwxyzASTUBCDhbijkEFGHIVWXJKLlcmnoMNO
178 presets: [
179 SwaggerUIBundle.presets.apis,
180 SwaggerUIBundle.SwaggerUIStandalonePreset
181 ],
182 })"""
184 if init_oauth: 1pqPQRrstdaefguvwxyzASTUBCDhbijkEFGHIVWXJKLlcmnoMNO
185 html += f""" 2r s d abB C h J K l
186 ui.initOAuth({_html_safe_json(jsonable_encoder(init_oauth))})
187 """
189 html += """ 1pqPQRrstdaefguvwxyzASTUBCDhbijkEFGHIVWXJKLlcmnoMNO
190 </script>
191 </body>
192 </html>
193 """
194 return HTMLResponse(html) 1pqPQRrstdaefguvwxyzASTUBCDhbijkEFGHIVWXJKLlcmnoMNO
197def get_redoc_html( 1YZ01
198 *,
199 openapi_url: Annotated[
200 str,
201 Doc(
202 """
203 The OpenAPI URL that ReDoc should load and use.
205 This is normally done automatically by FastAPI using the default URL
206 `/openapi.json`.
208 Read more about it in the
209 [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.
219 Read more about it in the
220 [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.
230 It is normally set to a CDN URL.
232 Read more about it in the
233 [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 alternative
256 API docs (normally served at `/redoc`).
258 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.
261 Read more about it in the
262 [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
263 """
264 html = f""" 12345a6789!#$%b'()*+,c-.
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: 12345a6789!#$%b'()*+,c-.
274 html += """ 12345a6789!#$%b'()*+,c-.
275 <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
276 """
277 html += f""" 12345a6789!#$%b'()*+,c-.
278 <link rel="shortcut icon" href="{redoc_favicon_url}">
279 <!--
280 ReDoc doesn't change outer page styles
281 -->
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) 12345a6789!#$%b'()*+,c-.
301def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse: 1YZ01
302 """
303 Generate the HTML response with the OAuth2 redirection for Swagger UI.
305 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.html
308 html = """ 1/:;=?@[]^_`{|}
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;
323 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 }
329 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 ) : {};
337 isValid = qp.state === sentState;
339 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 }
353 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 }
365 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 }
378 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) 1/:;=?@[]^_`{|}