Handler
Panoramica Rapida
Handler è un concetto fondamentale nel framework Salvo, che può essere semplicemente inteso come un'unità di elaborazione delle richieste. Ha due scopi principali:
-
Come Endpoint: Un oggetto che implementa
Handlerpuò essere inserito nel sistema di routing come endpoint finale per elaborare le richieste. Quando si utilizza la macro#[handler], una funzione può essere utilizzata direttamente come endpoint; mentre l'uso della macro#[endpoint]non solo consente di fungere da endpoint, ma genera automaticamente la documentazione OpenAPI (questo sarà dettagliato nella documentazione successiva). -
Come Middleware: Lo stesso
Handlerpuò anche essere utilizzato come middleware per elaborare le richieste prima o dopo che raggiungano l'endpoint finale.
Il flusso di elaborazione delle richieste di Salvo può essere visto come una "pipeline": una richiesta passa prima attraverso una serie di middleware (elaborazione verticale) e poi raggiunge l'endpoint corrispondente (elaborazione orizzontale). Sia i middleware che gli endpoint sono implementazioni di Handler, il che garantisce coerenza e flessibilità in tutto il sistema.
Diagramma di Flusso del Handler in Salvo
Middleware e il Modello a Cipolla
L'essenza del modello a cipolla è che posizionando ctrl.call_next() prima e dopo una logica specifica, si implementa un flusso di elaborazione bidirezionale per richieste e risposte, consentendo a ciascun middleware di partecipare al ciclo completo richiesta-risposta.
Struttura di Esempio Completa di Middleware
Cos'è un Handler
Un Handler è l'oggetto concreto responsabile dell'elaborazione degli oggetti Request. Handler stesso è un Trait che contiene un metodo asincrono handle:
La firma predefinita della funzione handle include quattro parametri, in ordine: &mut Request, &mut Depot, &mut Response, &mut FlowCtrl. Depot è una memoria temporanea che può contenere dati relativi alla richiesta corrente.
A seconda di come viene utilizzato, può fungere da middleware (hoop), che può eseguire elaborazioni prima o dopo che la richiesta raggiunga il Handler formale di elaborazione delle richieste, come ad esempio: verifica dell'accesso, compressione dei dati, ecc.
Il middleware viene aggiunto tramite la funzione hoop di un Router. Il middleware aggiunto influisce sul Router corrente e su tutti i suoi Router discendenti.
Un Handler può anche essere utilizzato come Handler che partecipa alla corrispondenza delle rotte e viene eseguito alla fine, noto come goal.
Handler come Middleware (hoop)
Quando un Handler agisce come middleware, può essere aggiunto ai seguenti tre tipi di oggetti che supportano middleware:
Service: Qualsiasi richiesta passerà attraverso il middleware nelService.Router: Solo quando la corrispondenza della rotta ha successo, la richiesta passerà attraverso il middleware definito nelServicee tutto il middleware raccolto lungo il percorso di corrispondenza.Catcher: Quando si verifica un errore e non sono state scritte informazioni di errore personalizzate, la richiesta passerà attraverso il middleware nelCatcher.Handler: Lo stessoHandlersupporta l'aggiunta di wrapper middleware per eseguire alcune logiche pre o post.
Utilizzo della Macro #[handler]
La macro #[handler] può semplificare notevolmente la scrittura del codice e migliorare la flessibilità del codice.
Può essere applicata a una funzione per farla implementare Handler:
Questo è equivalente a:
Come puoi vedere, con #[handler], il codice diventa molto più semplice:
- Non è necessario aggiungere manualmente
#[async_trait]. - I parametri non necessari nella funzione vengono omessi e i parametri richiesti possono essere disposti in qualsiasi ordine.
- Per gli oggetti che implementano le astrazioni
WriteroScribe, possono essere restituiti direttamente come valore di ritorno della funzione. Qui,&'static strimplementaScribe, quindi può essere restituito direttamente.
#[handler] può essere applicato non solo alle funzioni, ma anche al blocco impl di una struct per far sì che la struct implementi Handler. In questo caso, la funzione handle all'interno del blocco impl è riconosciuta come l'implementazione concreta del metodo handle in Handler:
Gestione degli Errori
In Salvo, un Handler può restituire un Result, a condizione che sia il tipo Ok che Err all'interno del Result implementino il trait Writer.
Considerando l'uso diffuso di anyhow, quando la feature anyhow è abilitata, anyhow::Error implementerà il trait Writer. anyhow::Error verrà mappato su InternalServerError.
Per i tipi di errore personalizzati, è possibile generare pagine di errore diverse secondo necessità.