Ir para o conteúdo

Roteamento

Roteamento é definido pelos decoradores nos handlers.

Parâmetros de caminho

Parâmetros podem ser definidos no caminho dos handlers utilizando a sintaxe :parameter_name, onde parameter_name deve ser o nome do argumento na assinatura do handler.

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}!")

Aqui foi usado Annotated e FromPath para indicar que o argumento do handler deve ser vinculado ao parâmetro do caminho da requisição. Mais sobre isso será explicado nas seções seguintes.

Correspondência de caminho

O comportamento padrão é o parâmetro de caminho corresponder a apenas um único seguimento. Se você quiser corresponder ao caminho completo, ou a um subcaminho do caminho da requisição, utilize a sintaxe *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}!")

Para uma requisição como GET hello/Python/World, o handler retornará Hello, Python World!.

Você pode combinar ambos os tipos de parâmetros sem problemas:

  • *path
  • *path/literal_segment
  • :normal_param/*path
  • :normal_param/*path/:other_path

Conversão de parâmetros

A conversão de parâmetros é realizada através de anotações de tipo nos parâmetros. O framework tentará encontrar um conversor adequado ao tipo do parâmetro e então converter o valor antes de chamar o 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)})

O framework procurará por um serviço que implementa selva.web.converter.from_request.FromRequest[FromPath] para recuperar os dados da requisição, então esse serviço procurará por um conversor, um serviço que implementa selva.web.converter.Converter[str, int] para converter os dados para o tipo requisitado.

Selva provê conversores para os tipos str, int, float, bool e pathlib.PurePath.

Coversão de parâmetros customizada

A conversão pode ser customizada ao prover uma implementação de selva.web.converter.Converter. Você normalmente utilizará como atalho o decorador 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))

Se a implementação de Converter lançar um erro, o handler não será chamado. E se o erro for uma subclasse de selva.web.error.HTTPError, por exemplo, HTTPUnauthorizedException, uma resposta será produzida de acordo com o erro.