| 2666 | self._mark_routes_changed() |
| 2667 | |
| 2668 | async def app(self, scope: Scope, receive: Receive, send: Send) -> None: |
| 2669 | assert scope["type"] in ("http", "websocket", "lifespan") |
| 2670 | |
| 2671 | if "router" not in scope: |
| 2672 | scope["router"] = self |
| 2673 | |
| 2674 | if scope["type"] == "lifespan": |
| 2675 | await self.lifespan(scope, receive, send) |
| 2676 | return |
| 2677 | |
| 2678 | partial: tuple[BaseRoute, Scope] | None = None |
| 2679 | for route in self.routes: |
| 2680 | match, child_scope = route.matches(scope) |
| 2681 | if match == Match.FULL: |
| 2682 | scope.update(child_scope) |
| 2683 | await route.handle(scope, receive, send) |
| 2684 | return |
| 2685 | if match == Match.PARTIAL and partial is None: |
| 2686 | partial = (route, child_scope) |
| 2687 | |
| 2688 | if partial is not None: |
| 2689 | route, child_scope = partial |
| 2690 | scope.update(child_scope) |
| 2691 | await route.handle(scope, receive, send) |
| 2692 | return |
| 2693 | |
| 2694 | route_path = get_route_path(scope) |
| 2695 | if scope["type"] == "http" and self.redirect_slashes and route_path != "/": |
| 2696 | redirect_scope = dict(scope) |
| 2697 | if route_path.endswith("/"): |
| 2698 | redirect_scope["path"] = redirect_scope["path"].rstrip("/") |
| 2699 | else: |
| 2700 | redirect_scope["path"] = redirect_scope["path"] + "/" |
| 2701 | |
| 2702 | for route in self.routes: |
| 2703 | match, _ = route.matches(redirect_scope) |
| 2704 | if match != Match.NONE: |
| 2705 | redirect_url = URL(scope=redirect_scope) |
| 2706 | response = RedirectResponse(url=str(redirect_url)) |
| 2707 | await response(scope, receive, send) |
| 2708 | return |
| 2709 | |
| 2710 | ( |
| 2711 | low_priority_match, |
| 2712 | low_priority_scope, |
| 2713 | low_priority_route, |
| 2714 | low_priority_context, |
| 2715 | ) = self._match_low_priority(scope) |
| 2716 | if low_priority_match != Match.NONE and low_priority_route is not None: |
| 2717 | _update_scope(scope, low_priority_scope) |
| 2718 | if low_priority_context is not None: |
| 2719 | _get_fastapi_scope(scope)[_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY] = ( |
| 2720 | low_priority_context |
| 2721 | ) |
| 2722 | original_route = low_priority_context.original_route |
| 2723 | if isinstance(original_route, APIRoute): |
| 2724 | scope["route"] = original_route |
| 2725 | await original_route.handle(scope, receive, send) |