Browse Source

Merge fe09a698bf into 313723494b

pull/13627/merge
Fatimah Alhumaidhi 2 days ago
committed by GitHub
parent
commit
9e68e99317
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 14
      docs/en/docs/advanced/middleware.md
  2. 61
      docs_src/advanced_middleware/tutorial004.py
  3. 132
      fastapi/applications.py
  4. 160
      fastapi/routing.py
  5. 55
      tests/test_tutorial/test_advanced_middleware/test_tutorial004.py

14
docs/en/docs/advanced/middleware.md

@ -84,6 +84,20 @@ The following arguments are supported:
* `minimum_size` - Do not GZip responses that are smaller than this minimum size in bytes. Defaults to `500`. * `minimum_size` - Do not GZip responses that are smaller than this minimum size in bytes. Defaults to `500`.
* `compresslevel` - Used during GZip compression. It is an integer ranging from 1 to 9. Defaults to `9`. Lower value results in faster compression but larger file sizes, while higher value results in slower compression but smaller file sizes. * `compresslevel` - Used during GZip compression. It is an integer ranging from 1 to 9. Defaults to `9`. Lower value results in faster compression but larger file sizes, while higher value results in slower compression but smaller file sizes.
## Router and Route-Level Middleware Example
**FastAPI supports adding scoped middleware per route and router. Middleware execution order:**
- **App‑level middleware** runs on *every* request as soon as it enters your application, before any router or route is matched.
- **Router‑level middleware** runs next, wrapping all requests to routes included on that router.
- **Route‑level middleware** runs last, just around the specific path operation.
This gives better control over where and when logic executes.
The example below shows middleware applied at each scope. Notice how the inner route’s middleware is able to match path params ;)
{* ../../docs_src/advanced_middleware/tutorial004.py *}
## Other middlewares ## Other middlewares
There are many other ASGI middlewares. There are many other ASGI middlewares.

61
docs_src/advanced_middleware/tutorial004.py

@ -0,0 +1,61 @@
from fastapi import APIRouter, FastAPI, Request
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
app = FastAPI()
@app.middleware("http")
async def app_middleware(request: Request, call_next):
print("App before")
response = await call_next(request)
print("App after")
return response
async def outer_middleware(request: Request, call_next):
print("Outer before")
response = await call_next(request)
print("Outer after")
return response
outer = APIRouter(
prefix="/outer",
middleware=[Middleware(BaseHTTPMiddleware, dispatch=outer_middleware)],
)
async def name_middleware(request: Request, call_next):
print(f"Hi {request.path_params.get('name')}!")
response = await call_next(request)
print(f"Bye {request.path_params.get('name')}!")
return response
inner = APIRouter(prefix="/inner")
@inner.get(
"/{name}",
middleware=[Middleware(BaseHTTPMiddleware, dispatch=name_middleware)],
)
async def hello(name: str):
print("Handler")
return {"message": f"Hello {name} from inner!"}
@outer.get("/")
async def outer_hello():
print("Handler")
return {"message": "Hello from outer!"}
@app.get("/")
async def app_hello():
print("Handler")
return {"message": "Hello from app!"}
outer.include_router(inner)
app.include_router(outer)

132
fastapi/applications.py

@ -1084,6 +1084,7 @@ class FastAPI(Starlette):
generate_unique_id_function: Callable[[routing.APIRoute], str] = Default( generate_unique_id_function: Callable[[routing.APIRoute], str] = Default(
generate_unique_id generate_unique_id
), ),
middleware: Optional[Sequence[Middleware]] = None,
) -> None: ) -> None:
self.router.add_api_route( self.router.add_api_route(
path, path,
@ -1110,6 +1111,7 @@ class FastAPI(Starlette):
name=name, name=name,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def api_route( def api_route(
@ -1140,6 +1142,7 @@ class FastAPI(Starlette):
generate_unique_id_function: Callable[[routing.APIRoute], str] = Default( generate_unique_id_function: Callable[[routing.APIRoute], str] = Default(
generate_unique_id generate_unique_id
), ),
middleware: Optional[Sequence[Middleware]] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
def decorator(func: DecoratedCallable) -> DecoratedCallable: def decorator(func: DecoratedCallable) -> DecoratedCallable:
self.router.add_api_route( self.router.add_api_route(
@ -1167,6 +1170,7 @@ class FastAPI(Starlette):
name=name, name=name,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
return func return func
@ -1788,6 +1792,21 @@ class FastAPI(Starlette):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP GET operation. Add a *path operation* using an HTTP GET operation.
@ -1828,6 +1847,7 @@ class FastAPI(Starlette):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def put( def put(
@ -2161,6 +2181,21 @@ class FastAPI(Starlette):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP PUT operation. Add a *path operation* using an HTTP PUT operation.
@ -2206,6 +2241,7 @@ class FastAPI(Starlette):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def post( def post(
@ -2539,6 +2575,21 @@ class FastAPI(Starlette):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP POST operation. Add a *path operation* using an HTTP POST operation.
@ -2584,6 +2635,7 @@ class FastAPI(Starlette):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def delete( def delete(
@ -2917,6 +2969,21 @@ class FastAPI(Starlette):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP DELETE operation. Add a *path operation* using an HTTP DELETE operation.
@ -2957,6 +3024,7 @@ class FastAPI(Starlette):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def options( def options(
@ -3290,6 +3358,21 @@ class FastAPI(Starlette):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP OPTIONS operation. Add a *path operation* using an HTTP OPTIONS operation.
@ -3330,6 +3413,7 @@ class FastAPI(Starlette):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def head( def head(
@ -3663,6 +3747,21 @@ class FastAPI(Starlette):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP HEAD operation. Add a *path operation* using an HTTP HEAD operation.
@ -3703,6 +3802,7 @@ class FastAPI(Starlette):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def patch( def patch(
@ -4036,6 +4136,21 @@ class FastAPI(Starlette):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP PATCH operation. Add a *path operation* using an HTTP PATCH operation.
@ -4081,6 +4196,7 @@ class FastAPI(Starlette):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def trace( def trace(
@ -4414,6 +4530,21 @@ class FastAPI(Starlette):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP TRACE operation. Add a *path operation* using an HTTP TRACE operation.
@ -4454,6 +4585,7 @@ class FastAPI(Starlette):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def websocket_route( def websocket_route(

160
fastapi/routing.py

@ -61,6 +61,7 @@ from pydantic import BaseModel
from starlette import routing from starlette import routing
from starlette.concurrency import run_in_threadpool from starlette.concurrency import run_in_threadpool
from starlette.exceptions import HTTPException from starlette.exceptions import HTTPException
from starlette.middleware import Middleware
from starlette.requests import Request from starlette.requests import Request
from starlette.responses import JSONResponse, Response from starlette.responses import JSONResponse, Response
from starlette.routing import ( from starlette.routing import (
@ -460,6 +461,7 @@ class APIRoute(routing.Route):
generate_unique_id_function: Union[ generate_unique_id_function: Union[
Callable[["APIRoute"], str], DefaultPlaceholder Callable[["APIRoute"], str], DefaultPlaceholder
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Optional[Sequence[Middleware]] = None,
) -> None: ) -> None:
self.path = path self.path = path
self.endpoint = endpoint self.endpoint = endpoint
@ -567,8 +569,13 @@ class APIRoute(routing.Route):
name=self.unique_id, name=self.unique_id,
embed_body_fields=self._embed_body_fields, embed_body_fields=self._embed_body_fields,
) )
self.middleware = middleware
self.app = request_response(self.get_route_handler()) self.app = request_response(self.get_route_handler())
if middleware is not None:
for cls, args, kwargs in reversed(middleware):
self.app = cls(self.app, *args, **kwargs)
def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]: def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]:
return get_request_handler( return get_request_handler(
dependant=self.dependant, dependant=self.dependant,
@ -834,6 +841,21 @@ class APIRouter(routing.Router):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this router.
Router-level middleware is executed after application-level middleware.
When multiple routers declare middleware, the outermost (furthest) router's middleware runs first.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
routes=routes, routes=routes,
@ -842,6 +864,7 @@ class APIRouter(routing.Router):
on_startup=on_startup, on_startup=on_startup,
on_shutdown=on_shutdown, on_shutdown=on_shutdown,
lifespan=lifespan, lifespan=lifespan,
middleware=middleware,
) )
if prefix: if prefix:
assert prefix.startswith("/"), "A path prefix must start with '/'" assert prefix.startswith("/"), "A path prefix must start with '/'"
@ -859,6 +882,7 @@ class APIRouter(routing.Router):
self.route_class = route_class self.route_class = route_class
self.default_response_class = default_response_class self.default_response_class = default_response_class
self.generate_unique_id_function = generate_unique_id_function self.generate_unique_id_function = generate_unique_id_function
self.middleware = middleware
def route( def route(
self, self,
@ -912,6 +936,7 @@ class APIRouter(routing.Router):
generate_unique_id_function: Union[ generate_unique_id_function: Union[
Callable[[APIRoute], str], DefaultPlaceholder Callable[[APIRoute], str], DefaultPlaceholder
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Optional[Sequence[Middleware]] = None,
) -> None: ) -> None:
route_class = route_class_override or self.route_class route_class = route_class_override or self.route_class
responses = responses or {} responses = responses or {}
@ -931,6 +956,9 @@ class APIRouter(routing.Router):
current_generate_unique_id = get_value_or_default( current_generate_unique_id = get_value_or_default(
generate_unique_id_function, self.generate_unique_id_function generate_unique_id_function, self.generate_unique_id_function
) )
if middleware and self.middleware:
middleware = list(self.middleware) + list(middleware)
route = route_class( route = route_class(
self.prefix + path, self.prefix + path,
endpoint=endpoint, endpoint=endpoint,
@ -958,6 +986,7 @@ class APIRouter(routing.Router):
callbacks=current_callbacks, callbacks=current_callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=current_generate_unique_id, generate_unique_id_function=current_generate_unique_id,
middleware=middleware or self.middleware,
) )
self.routes.append(route) self.routes.append(route)
@ -990,6 +1019,7 @@ class APIRouter(routing.Router):
generate_unique_id_function: Callable[[APIRoute], str] = Default( generate_unique_id_function: Callable[[APIRoute], str] = Default(
generate_unique_id generate_unique_id
), ),
middleware: Optional[Sequence[Middleware]] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
def decorator(func: DecoratedCallable) -> DecoratedCallable: def decorator(func: DecoratedCallable) -> DecoratedCallable:
self.add_api_route( self.add_api_route(
@ -1018,6 +1048,7 @@ class APIRouter(routing.Router):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
return func return func
@ -1329,6 +1360,7 @@ class APIRouter(routing.Router):
callbacks=current_callbacks, callbacks=current_callbacks,
openapi_extra=route.openapi_extra, openapi_extra=route.openapi_extra,
generate_unique_id_function=current_generate_unique_id, generate_unique_id_function=current_generate_unique_id,
middleware=route.middleware,
) )
elif isinstance(route, routing.Route): elif isinstance(route, routing.Route):
methods = list(route.methods or []) methods = list(route.methods or [])
@ -1695,6 +1727,21 @@ class APIRouter(routing.Router):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP GET operation. Add a *path operation* using an HTTP GET operation.
@ -1739,6 +1786,7 @@ class APIRouter(routing.Router):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def put( def put(
@ -2072,6 +2120,21 @@ class APIRouter(routing.Router):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP PUT operation. Add a *path operation* using an HTTP PUT operation.
@ -2121,6 +2184,7 @@ class APIRouter(routing.Router):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def post( def post(
@ -2454,6 +2518,21 @@ class APIRouter(routing.Router):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP POST operation. Add a *path operation* using an HTTP POST operation.
@ -2503,6 +2582,7 @@ class APIRouter(routing.Router):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def delete( def delete(
@ -2836,6 +2916,21 @@ class APIRouter(routing.Router):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP DELETE operation. Add a *path operation* using an HTTP DELETE operation.
@ -2880,6 +2975,7 @@ class APIRouter(routing.Router):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def options( def options(
@ -3213,6 +3309,21 @@ class APIRouter(routing.Router):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP OPTIONS operation. Add a *path operation* using an HTTP OPTIONS operation.
@ -3257,6 +3368,7 @@ class APIRouter(routing.Router):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def head( def head(
@ -3590,6 +3702,21 @@ class APIRouter(routing.Router):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP HEAD operation. Add a *path operation* using an HTTP HEAD operation.
@ -3639,6 +3766,7 @@ class APIRouter(routing.Router):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def patch( def patch(
@ -3972,6 +4100,21 @@ class APIRouter(routing.Router):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP PATCH operation. Add a *path operation* using an HTTP PATCH operation.
@ -4021,6 +4164,7 @@ class APIRouter(routing.Router):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
def trace( def trace(
@ -4354,6 +4498,21 @@ class APIRouter(routing.Router):
""" """
), ),
] = Default(generate_unique_id), ] = Default(generate_unique_id),
middleware: Annotated[
Optional[Sequence[Middleware]],
Doc(
"""
List of middleware to apply to all requests handled by this route.
Route-level middleware is executed after application-level and router-level middleware.
Any middleware declared on the router will be called before this route's middleware.
Middleware are applied in reverse order: the last middleware in this list is the first to be called.
Read more about it in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/)
"""
),
] = None,
) -> Callable[[DecoratedCallable], DecoratedCallable]: ) -> Callable[[DecoratedCallable], DecoratedCallable]:
""" """
Add a *path operation* using an HTTP TRACE operation. Add a *path operation* using an HTTP TRACE operation.
@ -4403,6 +4562,7 @@ class APIRouter(routing.Router):
callbacks=callbacks, callbacks=callbacks,
openapi_extra=openapi_extra, openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function, generate_unique_id_function=generate_unique_id_function,
middleware=middleware,
) )
@deprecated( @deprecated(

55
tests/test_tutorial/test_advanced_middleware/test_tutorial004.py

@ -0,0 +1,55 @@
from fastapi.testclient import TestClient
from docs_src.advanced_middleware.tutorial004 import app
client = TestClient(app)
def test_app_middleware_called_once(capsys):
r = client.get("/")
assert r.status_code == 200
captured = capsys.readouterr().out
assert captured.count("App before") == 1
assert captured.count("Outer before") == 0
assert captured.count("Handler") == 1
assert captured.count("Outer after") == 0
assert captured.count("App after") == 1
def test_outer_middleware_called_once(capsys):
r = client.get("/outer/")
assert r.status_code == 200
captured = capsys.readouterr().out
assert captured.count("App before") == 1
assert captured.count("Outer before") == 1
assert captured.count("Handler") == 1
assert captured.count("Outer after") == 1
assert captured.count("App after") == 1
def test_name_middleware_called_once(capsys):
name = "you"
r = client.get(f"/outer/inner/{name}")
assert r.status_code == 200
assert r.json() == {"message": f"Hello {name} from inner!"}
captured = capsys.readouterr().out
seq = [
"App before",
"Outer before",
f"Hi {name}!",
"Handler",
f"Bye {name}!",
"Outer after",
"App after",
]
for msg in seq:
assert captured.count(msg) == 1
idx = 0
for msg in seq:
next_idx = captured.find(msg, idx)
assert next_idx >= idx
idx = next_idx
Loading…
Cancel
Save