ActionFilter: tracciamento di request e response di un metodo MVC

Sviluppando una SPA (Single Page Application) ci si trova spesso a doversi confrontare con problemi di serializzazione e deserializzazione di dati in formato JSON (Javascript Object Notation), da e verso il server remoto. La tecnologia che ho scelto per il mio server-side backend è ASP.NET MVC: a mio parare la scelta più naturale e sensata per uno sviluppatore che ha un forte background .NET.

In fase di sviluppo, una delle attività che maggiormente fanno perdere tempo, è proprio comprendere perchè questo o quel metodo non risponde correttamente: errore nel passaggio dei parametri, problemi di formattazione, variabili e fields passati con la denominazione errata, etc. E la problematica si ingigantisce quando ci si trova a sviluppare il backend per dei colleghi che si occuperanno solo della parte client dell'applicativo: due mondi completamente isolati, dove la firma dei metodi server rappresenta l'unico "contratto" a garanzia delle due parti.

Ma i contratti e gli schemi, in una architettura RESTful, sono equiparabili a carta straccia; è per questo motivo che, dopo numerose ore spese ad analizzare (manualmente) le chieste client in ingresso nella mia piattaforma di servizi, mi sono deciso ad intraprende una strada che mi garantisse un tracciamento delle "request" e "response" anche in modalità non interattiva.

Scrivere un pezzo di codice in grado di innestarsi nella pipeline di ASP.NET MVC, non è poi così difficile grazie alla potenza degli "ActionFilter". Tantopiù che, nel framework Microsoft, questi ultimi sono stati concepiti con filosofia "AOP" (Aspect Oriented Programming) tali da poter essere applicati come semplici marcatori sopra i controller (o sulle action) sulle quali il comportamento è desiderato.

Partiamo con la definizione di una nuova classe, che con poca fantasia chiamiamo "TraceActionAttribute", derivante direttamente dal tipo base "ActionFilterAttribute". E' importante notare che l'infrastruttura della classe base ci permette di intercettare quattro diversi "eventi" (tramite override di metodi "virtual") che si verificano con la richiesta di una particolare action:
  • OnActionExecuting : sollevato prima dell'invocazione di una action su un controller
  • OnResultExecuting : sollevato prima della generazione di un risultato di una action
  • OnResultExecuted : sollevato dopo la generazione di un risultato di una action
  • OnActionExecuted : sollevato dopo l'esecuzione completa della action

In questa particolare implementazione ho scelto eseguire l'override dei due soli metodi di elaborazione della action: "OnActionExecuting" per tracciare la request proveniente dal client remoto, e "OnActionExecuted" per la response, analizzando l'emissione del risultato al chiamante. Inoltre, poichè capita spesso che un particolare client richieda autorizzazione (Windows, Forms, Basic, etc.), ho ritenuto comodo ottenere anche questi dati per tracciare una panoramica più completa ed esaustiva.

Su "OnActionExecuting", usando il contesto di esecuzione del filtro, recupero le informazioni relative al controller ed action invocate, le credenziali dell'utente (chiaramente se una autenticazione è stata specificata) e gli eventuali parametri della action stessa; il tutto viene memorizzato in proprietà "protected" globali al filtro per poter comodamente essere consumate anche in seguito.

Su "OnActionExecuted" invece mi sono concentrato sul recupero del risultato di elaborazione della action. Se si dovesse trattare di un "ActionResult" di tipo "Json" (type "JsonResult"), eseguiremo un tracciamento più dettagliato, serializzando i valori tramite i preziosi servigi nella fantastica libreria "Json.Net". Due metodi virtuali, "TraceRequest" e "TraceResponse", si prenderanno l'onere di eseguire il tracciamento effettivo delle due fasi dell'invocazione. In questo caso non ho voluto inserire alcuna dipendenza nel "filter" (a parte, naturalmente, quella di Json.Net): il trace avviene nell'output console dell'ambiente di sviluppo; ma il target può essere facilmente essere variato derivando la classe, in modo che si ridirezioni l'azione di scrittura su un file di log, oppure sulla base dati applicativa (secondo vostre esigenze).

Per controllare in maniera più "granulare" gli eventi, ho ritenuto utile applicare due proprietà pubbliche sulla classe "TraceActionAttribute": "EnableRequestTrace", che abilità/disabilità il tracciamento dell'evento di inizio richiesta, e "EnableResponseTrace" che ha la medesima funzione sulla risposta del server.

L'utilizzo? Niente di più banale: basta applicare l'attributo "TraceAction" sulla singola action che si vuole monitorare, o sull'interno controller, opportunamente impostando i flag di configurazione (che, lo ricordo, di default sono attivati). L'implementazione è ancora un po' grezza; ma funziona e mi sta facendo risparmiare un sacco di tempo. Per comodità ho raccolto questa (e altre) piccole utilità in un progettino in hosting su NuGet: Chakra.Mvc.

Potete pensare di esporre i log registrati da "TraceAction" direttamente su un particolare metodo ad uso diagnostica (es. "Diagnostics/RecentLogs"): i colleghi che utilizzeranno la vostra piattaforma di "WebAPI" saranno in grado di fare autodiagnostica delle chiamate che stanno utilizzando, riducendo al minimo il vostro coinvolgimento...con notevole riduzione dei travasi di bile per tutti i componenti del team!
M.

Commenti

Post popolari in questo blog

Restore di un database SQL Server in un container Docker

Cancellazione fisica vs cancellazione logica dei dati

WCF RESTful service: esposizione del servizio