|
15 | 15 |
|
16 | 16 | try: |
17 | 17 | from fastapi import FastAPI, Request, Response, Body |
18 | | - from fastapi.responses import JSONResponse |
| 18 | + from fastapi.responses import JSONResponse, RedirectResponse |
19 | 19 | from fastapi.staticfiles import StaticFiles |
20 | 20 | from starlette.responses import Response as StarletteResponse |
21 | 21 | from starlette.datastructures import MutableHeaders |
|
27 | 27 | Response = None |
28 | 28 | Body = None |
29 | 29 | JSONResponse = None |
| 30 | + RedirectResponse = None |
30 | 31 | StaticFiles = None |
31 | 32 | StarletteResponse = None |
32 | 33 | MutableHeaders = None |
@@ -115,6 +116,7 @@ def __init__(self, server: FastAPI): |
115 | 116 | self.server: FastAPI = server |
116 | 117 | self.error_handling_mode = "ignore" |
117 | 118 | self.request_adapter = FastAPIRequestAdapter |
| 119 | + self._before_request_funcs = [] |
118 | 120 | super().__init__() |
119 | 121 |
|
120 | 122 | def __call__(self, *args: Any, **kwargs: Any): |
@@ -213,9 +215,13 @@ def add_url_rule( |
213 | 215 | ) |
214 | 216 |
|
215 | 217 | def before_request(self, func: Callable[[], Any] | None): |
216 | | - # FastAPI does not have before_request, but we can use middleware |
217 | | - self.server.add_middleware(CurrentRequestMiddleware) |
218 | | - self.server.middleware("http")(self._make_before_middleware(func)) |
| 218 | + if func is not None: |
| 219 | + self._before_request_funcs.append(func) |
| 220 | + # Only add the middleware once |
| 221 | + if not hasattr(self, "_before_middleware_added"): |
| 222 | + self.server.add_middleware(CurrentRequestMiddleware) |
| 223 | + self.server.middleware("http")(self._make_before_middleware()) |
| 224 | + self._before_middleware_added = True |
219 | 225 |
|
220 | 226 | def after_request(self, func: Callable[[], Any] | None): |
221 | 227 | # FastAPI does not have after_request, but we can use middleware |
@@ -262,18 +268,20 @@ def make_response( |
262 | 268 | def jsonify(self, obj: Any): |
263 | 269 | return JSONResponse(content=obj) |
264 | 270 |
|
265 | | - def _make_before_middleware(self, _func: Callable[[], Any] | None): |
| 271 | + def _make_before_middleware(self): |
266 | 272 | async def middleware(request, call_next): |
| 273 | + for func in self._before_request_funcs: |
| 274 | + if inspect.iscoroutinefunction(func): |
| 275 | + await func() |
| 276 | + else: |
| 277 | + func() |
267 | 278 | try: |
268 | 279 | response = await call_next(request) |
269 | 280 | return response |
270 | 281 | except PreventUpdate: |
271 | | - # No content, nothing to update |
272 | 282 | return Response(status_code=204) |
273 | | - except (Exception) as e: # pylint: disable=broad-except |
274 | | - # Handle exceptions based on error_handling_mode |
| 283 | + except Exception as e: |
275 | 284 | if self.error_handling_mode in ["raise", "prune"]: |
276 | | - # Prune the traceback to remove internal Dash calls |
277 | 285 | tb = self._get_traceback(None, e) |
278 | 286 | return Response(content=tb, media_type="text/html", status_code=500) |
279 | 287 | return JSONResponse( |
@@ -338,6 +346,21 @@ async def serve(request: Request, package_name: str, fingerprinted_path: str): |
338 | 346 | name = "_dash-component-suites/{package_name}/{fingerprinted_path:path}" |
339 | 347 | dash_app._add_url(name, serve) # pylint: disable=protected-access |
340 | 348 |
|
| 349 | + def _create_redirect_function(self, redirect_to): |
| 350 | + def _redirect(): |
| 351 | + return RedirectResponse(url=redirect_to, status_code=301) |
| 352 | + |
| 353 | + return _redirect |
| 354 | + |
| 355 | + def add_redirect_rule(self, app, fullname, path): |
| 356 | + self.server.add_api_route( |
| 357 | + fullname, |
| 358 | + self._create_redirect_function(app.get_relative_path(path)), |
| 359 | + methods=["GET"], |
| 360 | + name=fullname, |
| 361 | + include_in_schema=False, |
| 362 | + ) |
| 363 | + |
341 | 364 | def dispatch(self, dash_app: Dash): |
342 | 365 | async def _dispatch(request: Request): |
343 | 366 | # pylint: disable=protected-access |
|
0 commit comments