from __future__ import annotations
from typing import TYPE_CHECKING, Any
from litestar._layers.utils import narrow_response_cookies, narrow_response_headers
from litestar.exceptions import ImproperlyConfiguredException
from litestar.types.empty import Empty
from litestar.utils import normalize_path
from litestar.utils.signature import add_types_to_signature_namespace
from litestar.utils.sync import ensure_async_callable
__all__ = ("Router",)
if TYPE_CHECKING:
from collections.abc import Mapping, Sequence
from litestar.connection import Request, WebSocket
from litestar.datastructures import CacheControlHeader, ETag
from litestar.dto import AbstractDTO
from litestar.openapi.spec import SecurityRequirement
from litestar.response import Response
from litestar.types import (
AfterRequestHookHandler,
AfterResponseHookHandler,
BeforeRequestHookHandler,
ControllerRouterHandler,
ExceptionHandlersMap,
Guard,
Middleware,
ParametersMap,
ResponseCookies,
TypeEncodersMap,
)
from litestar.types.callable_types import AsyncAfterRequestHookHandler, AsyncAfterResponseHookHandler
from litestar.types.composite_types import Dependencies, ResponseHeaders, TypeDecodersSequence
from litestar.types.empty import EmptyType
[docs]
class Router:
"""The Litestar Router class.
A Router instance is used to group controller, routers and route handler functions under a shared path fragment
"""
__slots__ = (
"after_request",
"after_response",
"before_request",
"cache_control",
"dependencies",
"dto",
"etag",
"exception_handlers",
"guards",
"include_in_schema",
"middleware",
"opt",
"parameters",
"path",
"registered_route_handler_ids",
"request_class",
"request_max_body_size",
"response_class",
"response_cookies",
"response_headers",
"return_dto",
"route_handlers",
"security",
"signature_namespace",
"tags",
"type_decoders",
"type_encoders",
"websocket_class",
)
[docs]
def __init__(
self,
path: str,
*,
after_request: AfterRequestHookHandler | None = None,
after_response: AfterResponseHookHandler | None = None,
before_request: BeforeRequestHookHandler | None = None,
cache_control: CacheControlHeader | None = None,
dependencies: Dependencies | None = None,
dto: type[AbstractDTO] | None | EmptyType = Empty,
etag: ETag | None = None,
exception_handlers: ExceptionHandlersMap | None = None,
guards: Sequence[Guard] | None = None,
include_in_schema: bool | EmptyType = Empty,
middleware: Sequence[Middleware] | None = None,
opt: Mapping[str, Any] | None = None,
parameters: ParametersMap | None = None,
request_class: type[Request] | None = None,
response_class: type[Response] | None = None,
response_cookies: ResponseCookies | None = None,
response_headers: ResponseHeaders | None = None,
return_dto: type[AbstractDTO] | None | EmptyType = Empty,
route_handlers: Sequence[ControllerRouterHandler],
security: Sequence[SecurityRequirement] | None = None,
signature_namespace: Mapping[str, Any] | None = None,
signature_types: Sequence[Any] | None = None,
tags: Sequence[str] | None = None,
type_decoders: TypeDecodersSequence | None = None,
type_encoders: TypeEncodersMap | None = None,
websocket_class: type[WebSocket] | None = None,
request_max_body_size: int | None | EmptyType = Empty,
) -> None:
"""Initialize a ``Router``.
Args:
after_request: A sync or async function executed before a :class:`Request <.connection.Request>` is passed
to any route handler. If this function returns a value, the request will not reach the route handler,
and instead this value will be used.
after_response: A sync or async function called after the response has been awaited. It receives the
:class:`Request <.connection.Request>` object and should not return any values.
before_request: A sync or async function called immediately before calling the route handler. Receives
the :class:`litestar.connection.Request` instance and any non-``None`` return value is used for the
response, bypassing the route handler.
cache_control: A ``cache-control`` header of type
:class:`CacheControlHeader <.datastructures.CacheControlHeader>` to add to route handlers of
this router. Can be overridden by route handlers.
dependencies: A string keyed mapping of dependency :class:`Provide <.di.Provide>` instances.
dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for (de)serializing and
validation of request data.
etag: An ``etag`` header of type :class:`ETag <.datastructures.ETag>` to add to route handlers of this app.
exception_handlers: A mapping of status codes and/or exception types to handler functions.
guards: A sequence of :data:`Guard <.types.Guard>` callables.
include_in_schema: A boolean flag dictating whether the route handler should be documented in the OpenAPI schema.
middleware: A sequence of :data:`Middleware <.types.Middleware>`.
opt: A string keyed mapping of arbitrary values that can be accessed in :data:`Guards <.types.Guard>` or
wherever you have access to :class:`Request <.connection.Request>` or
:data:`ASGI Scope <.types.Scope>`.
parameters: A mapping of :func:`Parameter <.params.Parameter>` definitions available to all application
paths.
path: A path fragment that is prefixed to all route handlers, controllers and other routers associated
with the router instance.
request_class: A custom subclass of :class:`Request <.connection.Request>` to be used as the default for
all route handlers, controllers and other routers associated with the router instance.
request_max_body_size: Maximum allowed size of the request body in bytes. If this size is exceeded,
a '413 - Request Entity Too Large" error response is returned.
response_class: A custom subclass of :class:`Response <.response.Response>` to be used as the default for
all route handlers, controllers and other routers associated with the router instance.
response_cookies: A sequence of :class:`Cookie <.datastructures.Cookie>` instances.
response_headers: A string keyed mapping of :class:`ResponseHeader <.datastructures.ResponseHeader>`
instances.
return_dto: :class:`AbstractDTO <.dto.base_dto.AbstractDTO>` to use for serializing
outbound response data.
route_handlers: A required sequence of route handlers, which can include instances of
:class:`Router <.router.Router>`, subclasses of :class:`Controller <.controller.Controller>` or any
function decorated by the route handler decorators.
security: A sequence of dicts that will be added to the schema of all route handlers in the application.
See :data:`SecurityRequirement <.openapi.spec.SecurityRequirement>`
for details.
signature_namespace: A mapping of names to types for use in forward reference resolution during signature modelling.
signature_types: A sequence of types for use in forward reference resolution during signature modelling.
These types will be added to the signature namespace using their ``__name__`` attribute.
tags: A sequence of string tags that will be appended to the schema of all route handlers under the
application.
type_decoders: A sequence of tuples, each composed of a predicate testing for type identity and a msgspec hook for deserialization.
type_encoders: A mapping of types to callables that transform them into types supported for serialization.
websocket_class: A custom subclass of :class:`WebSocket <.connection.WebSocket>` to be used as the default for
all route handlers, controllers and other routers associated with the router instance.
"""
self.after_request: AsyncAfterRequestHookHandler | None = (
ensure_async_callable(after_request) if after_request else None # type: ignore[assignment]
)
self.after_response: AsyncAfterResponseHookHandler | None = ( # pyright: ignore
ensure_async_callable(after_response) if after_response else None
)
self.before_request = ensure_async_callable(before_request) if before_request else None
self.cache_control = cache_control
self.dto = dto
self.etag = etag
self.dependencies = dict(dependencies or {})
self.exception_handlers = dict(exception_handlers or {})
self.guards = tuple(guards or [])
self.include_in_schema = include_in_schema
self.middleware = list(middleware or [])
self.opt = dict(opt or {})
self.parameters = dict(parameters or {})
self.path = normalize_path(path)
self.request_class = request_class
self.response_class = response_class
self.response_cookies = narrow_response_cookies(response_cookies) if response_cookies else ()
self.response_headers = narrow_response_headers(response_headers) if response_headers else ()
self.return_dto = return_dto
self.security = list(security or [])
self.signature_namespace = add_types_to_signature_namespace(
signature_types or [], dict(signature_namespace or {})
)
self.tags = list(tags or [])
self.registered_route_handler_ids: set[int] = set()
self.type_encoders = dict(type_encoders) if type_encoders is not None else {}
self.type_decoders = list(type_decoders) if type_decoders is not None else ()
self.websocket_class = websocket_class
self.request_max_body_size = request_max_body_size
self.route_handlers = tuple(route_handlers)
[docs]
def register(self, value: ControllerRouterHandler) -> None:
"""Register a Controller, Route instance or RouteHandler on the router"""
if value is self:
raise ImproperlyConfiguredException("Cannot register a router on itself")
self.route_handlers = (*self.route_handlers, value)