Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ASGI root_path for openapi docs #1199

Merged
merged 11 commits into from
Jun 11, 2020
Prev Previous commit
Next Next commit
♻️ Refactor application root_path logic, use root_path, deprecate ope…
…napi_prefix
  • Loading branch information
tiangolo committed Jun 11, 2020
commit dc4c05c19a570fb7cba104581b94f7d25a83c630
36 changes: 22 additions & 14 deletions fastapi/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
request_validation_exception_handler,
)
from fastapi.exceptions import RequestValidationError
from fastapi.logger import logger
from fastapi.openapi.docs import (
get_redoc_html,
get_swagger_ui_html,
Expand Down Expand Up @@ -36,7 +37,6 @@ def __init__(
description: str = "",
version: str = "0.1.0",
openapi_url: Optional[str] = "/openapi.json",
openapi_prefix: str = "",
default_response_class: Type[Response] = JSONResponse,
docs_url: Optional[str] = "/docs",
redoc_url: Optional[str] = "/redoc",
Expand All @@ -46,6 +46,8 @@ def __init__(
exception_handlers: Dict[Union[int, Type[Exception]], Callable] = None,
on_startup: Sequence[Callable] = None,
on_shutdown: Sequence[Callable] = None,
openapi_prefix: str = "",
root_path: str = "",
**extra: Dict[str, Any],
) -> None:
self.default_response_class = default_response_class
Expand All @@ -68,7 +70,15 @@ def __init__(
self.description = description
self.version = version
self.openapi_url = openapi_url
self._openapi_prefix = openapi_prefix.rstrip("/")
# TODO: remove when discarding the openapi_prefix parameter
if openapi_prefix:
logger.warning(
'"openapi_prefix" has been deprecated in favor of "root_path", which '
"follows more closely the ASGI standard, is simpler, and more "
"automatic. Check the docs at "
"https://fastapi.tiangolo.com/advanced/sub-applications-proxy/"
)
self.root_path = root_path or openapi_prefix
self.docs_url = docs_url
self.redoc_url = redoc_url
self.swagger_ui_oauth2_redirect_url = swagger_ui_oauth2_redirect_url
Expand All @@ -87,11 +97,7 @@ def __init__(
self.openapi_schema: Optional[Dict[str, Any]] = None
self.setup()

def get_openapi_prefixed(self, req: Request, path: Optional[str] = "") -> str:
prefix = self._openapi_prefix or req.scope.get("root_path", "").rstrip("/")
return prefix + (path or "")

def openapi(self, openapi_prefix: str) -> Dict:
def openapi(self, openapi_prefix: str = "") -> Dict:
if not self.openapi_schema:
self.openapi_schema = get_openapi(
title=self.title,
Expand All @@ -107,19 +113,18 @@ def setup(self) -> None:
if self.openapi_url:

async def openapi(req: Request) -> JSONResponse:
openapi_prefix = self.get_openapi_prefixed(req)
return JSONResponse(self.openapi(openapi_prefix))
root_path = req.scope.get("root_path", "").rstrip("/")
return JSONResponse(self.openapi(root_path))

self.add_route(self.openapi_url, openapi, include_in_schema=False)
if self.openapi_url and self.docs_url:

async def swagger_ui_html(req: Request) -> HTMLResponse:
openapi_url = self.get_openapi_prefixed(req, self.openapi_url)
root_path = req.scope.get("root_path", "").rstrip("/")
openapi_url = root_path + self.openapi_url
oauth2_redirect_url = self.swagger_ui_oauth2_redirect_url
if oauth2_redirect_url:
oauth2_redirect_url = self.get_openapi_prefixed(
req, oauth2_redirect_url
)
oauth2_redirect_url = root_path + oauth2_redirect_url
return get_swagger_ui_html(
openapi_url=openapi_url,
title=self.title + " - Swagger UI",
Expand All @@ -142,7 +147,8 @@ async def swagger_ui_redirect(req: Request) -> HTMLResponse:
if self.openapi_url and self.redoc_url:

async def redoc_html(req: Request) -> HTMLResponse:
openapi_url = self.get_openapi_prefixed(req, self.openapi_url)
root_path = req.scope.get("root_path", "").rstrip("/")
openapi_url = root_path + self.openapi_url
return get_redoc_html(
openapi_url=openapi_url, title=self.title + " - ReDoc"
)
Expand All @@ -154,6 +160,8 @@ async def redoc_html(req: Request) -> HTMLResponse:
)

async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if self.root_path:
scope["root_path"] = self.root_path
if AsyncExitStack:
async with AsyncExitStack() as stack:
scope["fastapi_astack"] = stack
Expand Down