Routing
Routing is defined by the decorators in the handler functions.
Path parameters
Parameters can be defined in the handler's path using the syntax :parameter_name
,
where parameter_name
must be the name of the argument on the handler's signature.
from typing import Annotated
from asgikit.requests import Request
from asgikit.responses import respond_text
from selva.web import get, FromPath
@get("hello/:name")
async def handler(request: Request, name: Annotated[str, FromPath]):
await respond_text(request.response, f"Hello, {name}!")
Here was used Annotated
and FromPath
to indicated that the handler argument
is to be bound to the parameter from the request path. More on that will be explained
in the following sections.
Path matching
The default behavior is for a path parameter to match a single path segment.
If you want to match the whole path, or a subpath of the request path,
use the syntax *parameter_name
.
from typing import Annotated
from asgikit.requests import Request
from asgikit.responses import respond_text
from selva.web import get, FromPath
@get("hello/*path")
async def handler(request: Request, path: Annotated[str, FromPath]):
name = " ".join(path.split("/"))
await respond_text(request.response, f"Hello, {name}!")
For a request like GET hello/Python/World
, the handler will output
Hello, Python World!
.
You can mix both types of parameters with no problem:
*path
*path/literal_segment
:normal_param/*path
:normal_param/*path/:other_path
Parameter conversion
Parameter conversion is done through the type annotation on the parameter. The framework will try to find a converter suitable for the parameter type and then convert the value before calling the handler.
from typing import Annotated
from asgikit.requests import Request
from asgikit.responses import respond_json
from selva.web import get, FromPath
@get("repeat/:amount")
async def handler(request: Request, amount: Annotated[int, FromPath]):
await respond_json(request.response, {f"repeat {i}": i for i in range(amount)})
The framework will look for a service implementing selva.web.converter.from_request.FromRequest[FromPath]
in order to get the data from the request, then this service will look for a converter,
a service implementing selva.web.converter.Converter[str, int]
to convert the
data to the requested type.
Selva already provide converters for the types str
, int
, float
, bool
and pathlib.PurePath
.
Custom parameter conversion
Conversion can be customized by providing an implementing of selva.web.converter.Converter
.
You normally use the shortcut decorator selva.web.converter.decorator.register_converter.
from dataclasses import dataclass
from typing import Annotated
from asgikit.requests import Request
from asgikit.responses import respond_text
from selva.web import get, FromPath
from selva.web.converter.decorator import register_converter
@dataclass
class MyModel:
name: str
@register_converter(str, MyModel)
class MyModelParamConverter:
def convert(self, value: str) -> MyModel:
return MyModel(value)
@get("/:model")
async def handler(request: Request, model: Annotated[MyModel, FromPath]):
await respond_text(request.response, str(model))
If the Converter
implementation raise an error, the handler is not called.
And if the error is a subclass of selva.web.error.HTTPError
, for instance
HTTPUnauthorizedException
, a response will be produced according to the error.