Nello script precedente (https://www.aspitalia.com/script/1285/Aumentare-Scalabilita-ASP.NET-Core-Web-API-Caching-Client-Side.aspx) abbiamo visto come il caching client side sia uno dei modi più efficaci per aumentare la scalabilità del nostro strato di servizi: quando è possibile, i client possono mantenere una cache locale delle risposte così da evitare del tutto la chiamata al server.
Il problema fondamentale di questo approccio, tuttavia, è che perdiamo il controllo di quanto memorizzato sul client, fintanto che non scada il TTL che abbiamo settato tramite l'header cache-control: se i dati dovessero cambiare, infatti, non abbiamo infatti un sistema per invalidare le cache dei client dal server. Quindi per forza di cose il TTL è tipicamente piuttosto basso, in modo da essere efficace per chiamate ripetute, ma comunque garantire un ragionevole refresh rate delle risposte. Questo fa sì che comunque i client, a intervalli regolari, invalideranno la loro cache, chiameranno il server e, pertanto, consumeremo banda.
Un sistema molto utile per mitigare questo problema è l'utilizzo di ETag, uno standard HTTP il cui principio di funzionamento è descritto nell'immagine in basso.
L'ETag è un header della risposta HTTP che contiene tipicamente un identificativo o un hash. Esso viene generato dal server e mantenuto in cache dal client. Quando il client effettuerà nuovamente la richiesta, invierà in allegato l'ETag all'interno dell'header If-None-Match. Se il valore corrisponde alla versione corrente sul server, quest'ultimo ritornerà una risposta di tipo 304 - Not Modified, che non contiene alcun body (e pertanto è molto leggera e veloce) e che istruirà il client a mantenere i dati precedenti in cache per un ulteriore TTL.
ASP.NET Core Web API non supporta direttamente questa funzionalità, ma possiamo implementarla facilmente grazie a un Action Filter come il seguente:
public class EnableETagAttribute : Attribute, IActionFilter { public void OnActionExecuted(ActionExecutedContext context) { var clientEtag = context.HttpContext.Request.Headers["If-None-Match"].FirstOrDefault(); var result = context.Result as ObjectResult; if (result == null) return; var serverEtag = JsonConvert.SerializeObject(result.Value).GetHashCode().ToString(); if (serverEtag == clientEtag) { context.Result = new StatusCodeResult(304); } context.HttpContext.Response.Headers.Add("Etag", serverEtag); } public void OnActionExecuting(ActionExecutingContext context) { } }
Questo filtro usa come ETag l'hash .NET della risposta, ma possiamo utilizzare qualsiasi logica per estrarre una stringa che sia univocamente verificabile. Come prima cosa, recuperiamo il contenuto dell'header If-None-Match dalla request e successivamente, calcoliamo l'hash di quanto restituito dalla Action. Se i due valori corrispondono, modifichiamo la risposta del server in un 304. Il valore dell'ETag è poi in ogni caso ritornato come header.
La peculiarità di questo filtro è che funziona con qualsiasi Action o Controller di Web API: per utilizzarlo, infatti, è sufficiente aggiungerlo, avendo cura di impostare anche il caching client side come abbiamo visto nel precedente script:
[HttpGet, EnableETag, ResponseCache(Location = ResponseCacheLocation.Any, Duration = 30)] public async Task<IEnumerable<string>> Get() { // .. altro codice qui .. }
Un aspetto importante da sottolineare, tuttavia, è che l'action viene comunque eseguita in ogni caso. Quindi questa tecnica ottimizza l'utilizzo di banda, ma non il carico lato server. Una possibile alternativa, potrebbe essere quella di mantenere uno storage in cache degli ETag validi, così da evitare interamente il calcolo della risposta. Ovviamente una soluzione di questo tipo non è però sempre attuabile e, soprattutto, va tipicamente gestita caso per caso nella logica della action stessa, invece che con un filtro esterno.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Short-circuiting della Pipeline in ASP.NET Core
Implementare il throttling in ASP.NET Core
Utilizzare HiLo per ottimizzare le insert in un database con Entity Framework
Creare form tipizzati con Angular
Utilizzare politiche di resiliency con Azure Container App
Ottimizzazione dei block template in Angular 17
Miglioramenti nell'accessibilità con Angular CDK
Cambiare la chiave di partizionamento di Azure Cosmos DB
Implementare l'infinite scroll con QuickGrid in Blazor Server
Eseguire una query su SQL Azure tramite un workflow di GitHub
Come EF 8 ha ottimizzato le query che usano il metodo Contains
Potenziare Azure AI Search con la ricerca vettoriale
I più letti di oggi
- Utilizzare Docker Compose con Azure App Service
- Modernizzare le applicazioni WPF e Windows Forms con Blazor
- annunciato #netstandard 2.1. .NET Core lo supporterà a partire da #netcore3, così come le prossime versione di #xamarin, #mono e #unity.il supporto per #netfx 4.8, invece, non ci sarà. https://aspit.co/bq2
- Steel Style CheckBox per Silverlight 4.0
- Utilizzare QuickGrid di Blazor con Entity Framework