Sempre più spesso, sviluppare un qualsiasi tipo di applicazione web o Windows significa anche attingere dati da servizi remoti, consumabili comodamente attraverso una API accessibile via web. Questa pratica è diventata così comune che ha suggerito la creazione di marketplace specifici.
Una Web API che offra dati rilevanti per il pubblico, quindi, può facilmente diventare un mattone fondamentale per il successo di applicazioni sviluppate da terze parti.
Uno dei passi da affrontare per rendere profittevole la nostra Web API, consiste nell'identificare dei livelli di servizio che, a tariffe via via crescenti, soddisfino le esigenze di budget di chi ha bisogno di farne un uso saltuario da chi invece ne vuol fare un uso massivo.
Il pacchetto WebApiThrottle ci aiuta nel limitare la frequenza con cui ogni client può rivolgere richieste alla nostra applicazione ASP.NET Web API.
Porre dei limiti non è solo un meccanismo di protezione contro l'aumento indiscriminato di richieste, ma è anche un modo per ottimizzare le risorse e garantire una capacità più elevata a coloro che hanno acquistato un livello di servizio superiore (ed incentivare gli altri a fare altrettanto).
Il pacchetto permette di distinguere i client secondo una qualsiasi combinazione dei seguenti criteri:
- la API Key fornita;
- l'indirizzo IP di provenienza;
- l'URL della action alla quale è rivolta la richiesta.
In questo esempio esploreremo il primo criterio, ovvero limiteremo le richieste dei client in base alla API Key che abbiamo rilasciato loro.
Iniziamo digitando quanto segue dalla Package Manager Console di Visual Studio 2015:
Install-Package WebApiThrottle
Ora apriamo il file /App_Start/WebApiConfig.cs e nel suo metodo Register aggiungiamo ai MessageHandlers un'istanza di ThrottlingHandler così configurata:
// Il ThrottlingHandler si occuperà di bloccare le richieste troppo frequenti, // prima ancora che vengano elaborate dalle action della nostra Web API config.MessageHandlers.Add(new ThrottlingHandler() { // Questi sono i limiti di default per i client che non forniscono una API key. // Possiamo indicare un numero di richieste al secondo, al minuto, // all'ora, al giorno e/o alla settimana Policy = new ThrottlePolicy(perSecond: 1, perMinute: 30, perHour: 100) { // Abilitiamo la limitazione dei clienti sulla base delle loro API Key ClientThrottling = true, // Indichiamo delle limitazioni meno restrittive // per queste due specifiche API Key ClientRules = new Dictionary<string, RateLimits> { { "APIKeyClient1", new RateLimits { PerSecond = 2, PerMinute = 100, PerHour = 1000 } }, { "APIKeyClient2", new RateLimits { PerSecond = 2, PerMinute = 200, PerHour = 5000 } }, } }, // I contatori di utilizzo della Web API (anche chiamati "metriche") // verranno mantenuti in cache Repository = new CacheRepository(), // Facoltativo: manteniamo uno storico delle richieste bloccate. // Per questo usiamo il tracing di ASP.Net Web API // Per attivarlo, si veda: // https://www.aspitalia.com/script/1198/Attivare-Tracing-ASP.NET-Web-API.aspx Logger = new TracingThrottleLogger(config.EnableSystemDiagnosticsTracing()) });
A questo punto, la nostra applicazione ASP.NET Web API è già in grado di regolare il volume di richieste in ingresso e di limitare quei client che ne stanno inviando in numero eccessivo.
I client che intendono consumare la nostra Web API, forniranno la propria API Key con l'intestazione Authorization-Token della richiesta, come si vede in questo esempio:
GET http://www.example.com/api/CambioValute HTTP/1.1 Host: www.example.com Accept: application/json Authorization-Token: APIKeyClient1
All'arrivo di ogni richiesta, il ThrottlingHandler aggiornerà le proprie metriche, ovvero un insieme di contatori conservati nell'oggetto Cache (o MemoryCache) di ASP.NET che tracciano l'attività dei client nel tempo. Tali metriche possono anche essere preservate dopo un riavvio dell'applicazione se forniamo un diverso meccanismo di persistenza che implementi IThrottleRepository.
Fintanto che il client invia richieste con una frequenza rispettosa dei limiti configurati, allora otterrà normali risposte dall'applicazione. Se, al contrario, dovesse superare tali limiti, allora il ThrottlingHandler inizierà a bloccare le richieste e risponderà immediatamente con un output simile al seguente:
HTTP/1.1 429 Content-Type: application/json; charset=utf-8 Retry-After: 9 Date: Sat, 03 Oct 2015 11:05:55 GMT Content-Length: 61 "API calls quota exceeded! maximum admitted 100 per Minute."
In questo esempio, il client viene informato del blocco con il codice di errore HTTP 429 (Too Many Requests) e con un messaggio che gli ricorda la limitazione da rispettare.
E' anche importante notare come l'intestazione Retry-After suggerisca il numero di secondi da attendere prima che la Web API sia di nuovo disponibile a fornire risposte.
Ovviamente nulla vieta ad un client di continuare ad inviare richieste che verranno bloccate ma, con la proprietà StackBlockedRequests della ThrottlePolicy, possiamo dissuadere questo comportamento. Grazie ad essa, infatti, anche le richieste bloccate andranno ad influire sulle metriche e perciò ad allungare ulteriormente il tempo di attesa.
Ogni richiesta bloccata può essere tracciata grazie al sistema di Tracing di ASP.NET Web API, così da monitorare l'attività dei client. A tal proposito, associamo un'implementazione di IThrottleLogger alla proprietà Logger della ThrottlePolicy ed otterremo righe di log come la seguente:
10/03/2015 08:39:19 Request 4ED76E1F9FC5BE33062309158154A2FC8CAEDB88 from 172.16.23.45 has been throttled (blocked), quota 10/Minute exceeded by 1
Il pacchetto WebApiThrottle offre molte altre possibilità di configurazione e vari punti di estendibilità. Per scenari d'uso avanzati, che prevedano il whitelisting di alcuni client o limitazioni applicate puntualmente sulle singole action, l'autore Stefan Prodan offre un'approfondita documentazione nel suo repository GitHub: [url]https://github.com/stefanprodan/WebApiThrottle[/url]
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Esporre i propri servizi applicativi con Semantic Kernel e ASP.NET Web API
Utilizzare i primary constructor di C# per inizializzare le proprietà
Creazione di componenti personalizzati in React.js con Tailwind CSS
Ottimizzare la latenza in Blazor 8 tramite InteractiveAuto render mode
Evitare (o ridurre) il repo-jacking sulle GitHub Actions
Usare un KeyedService di default in ASP.NET Core 8
Migliorare la scalabilità delle Azure Function con il Flex Consumption
Aprire una finestra di dialogo per selezionare una directory in WPF e .NET 8
Miglioramenti nelle performance di Angular 16
Popolare una classe a partire dal testo, con Semantic Kernel e ASP.NET Core Web API
Creazione di plugin per Tailwind CSS: espandere le Funzionalità del Framework
Come migrare da una form non tipizzata a una form tipizzata in Angular