Handler
Visão Geral Rápida
Handler é um conceito central no framework Salvo, que pode ser entendido simplesmente como uma unidade de processamento de requisições. Ele tem dois propósitos principais:
-
Como um Endpoint: Um objeto que implementa
Handlerpode ser colocado no sistema de roteamento como o endpoint final para processar requisições. Ao usar a macro#[handler], uma função pode ser usada diretamente como um endpoint; enquanto o uso da macro#[endpoint]não só permite que sirva como endpoint, mas também gera automaticamente a documentação OpenAPI (isso será detalhado na documentação subsequente). -
Como Middleware: O mesmo
Handlertambém pode ser usado como middleware para processar requisições antes ou depois que elas cheguem ao endpoint final.
O fluxo de processamento de requisições do Salvo pode ser visto como um "pipeline": uma requisição primeiro passa por uma série de middlewares (processamento vertical) e depois chega ao endpoint correspondente (processamento horizontal). Tanto middleware quanto endpoints são implementações de Handler, o que garante consistência e flexibilidade em todo o sistema.
Fluxograma do Handler no Salvo
Middleware e o Modelo Cebola
A essência do modelo cebola é que, ao colocar ctrl.call_next() antes e depois da lógica específica, ele implementa um fluxo de processamento bidirecional para requisições e respostas, permitindo que cada middleware participe do ciclo completo de requisição-resposta.
Estrutura de Exemplo Completo de Middleware
O que é um Handler
Um Handler é o objeto concreto responsável por processar objetos Request. O próprio Handler é um Trait contendo um método assíncrono handle:
A assinatura padrão da função handle inclui quatro parâmetros, na ordem: &mut Request, &mut Depot, &mut Response, &mut FlowCtrl. Depot é um armazenamento temporário que pode conter dados relacionados à requisição atual.
Dependendo de como é usado, pode servir como middleware (hoop), que pode executar processamento antes ou depois que a requisição chegue ao Handler formal de processamento de requisições, como: verificação de login, compactação de dados, etc.
O middleware é adicionado através da função hoop de um Router. O middleware adicionado afeta o Router atual e todos os seus Routers descendentes.
Um Handler também pode ser usado como um Handler que participa da correspondência de rota e é finalmente executado, conhecido como goal.
Handler como Middleware (hoop)
Quando um Handler atua como middleware, ele pode ser adicionado aos três tipos de objetos que suportam middleware:
Service: Qualquer requisição passará pelo middleware noService.Router: Somente quando a correspondência de rota for bem-sucedida, a requisição passará pelo middleware definido noServicee todo o middleware coletado ao longo do caminho correspondente.Catcher: Quando ocorre um erro e nenhuma informação de erro personalizada foi escrita, a requisição passará pelo middleware noCatcher.Handler: O próprioHandlersuporta a adição de wrappers de middleware para executar alguma lógica pré ou pós.
Usando a Macro #[handler]
A macro #[handler] pode simplificar muito a escrita de código e melhorar a flexibilidade do código.
Pode ser aplicada a uma função para fazê-la implementar Handler:
Isso é equivalente a:
Como você pode ver, com #[handler], o código se torna muito mais simples:
- Não é necessário adicionar manualmente
#[async_trait]. - Parâmetros desnecessários na função são omitidos, e os parâmetros necessários podem ser organizados em qualquer ordem.
- Para objetos que implementam as abstrações
WriterouScribe, eles podem ser retornados diretamente como valor de retorno da função. Aqui,&'static strimplementaScribe, então pode ser retornado diretamente.
#[handler] pode não só ser aplicado a funções, mas também ao bloco impl de uma struct para fazer a struct implementar Handler. Nesse caso, a função handle dentro do bloco impl é reconhecida como a implementação concreta do método handle em Handler:
Tratamento de Erros
No Salvo, um Handler pode retornar um Result, desde que tanto o tipo Ok quanto Err dentro do Result implementem o trait Writer.
Considerando o uso generalizado do anyhow, quando o recurso anyhow está ativado, anyhow::Error implementará o trait Writer. anyhow::Error será mapeado para InternalServerError.
Para tipos de erro personalizados, você pode gerar diferentes páginas de erro conforme necessário.