ASP.NET Web API si rivela un'ottima tecnologia per esporre un servizio ai nostri utenti remoti. Tuttavia, le latenze di rete e l'inevitabile overhead del protocollo HTTP potrebbero limitare la loro capacità di inviare numeri elevati di richieste nell'unità di tempo.
Il supporto al batching di ASP.NET Web API 2 è un efficace rimedio a questo problema, perché consente l'invio di molteplici richieste HTTP con un singolo round-trip al server.
Il guadagno prestazionale non è facilmente stimabile a causa di vari fattori ma, in linea generale, a trarne maggior beneficio saranno quei client che inviano al servizio molte piccole richieste in rapida sequenza.
Di per sé, il batching non implica che le richieste verranno eseguite nell'ambito di una transazione. Piuttosto, il suo scopo è quello di farci raggiungere un throughput più elevato, opzionalmente mantenendo un'esecuzione sequenziale delle richieste.
Lato server, andiamo a configurare un'apposita route da destinare a questo scopo. Aggiungiamo le seguenti linee di codice al corpo del metodo Register, che si trova nel file App_Start/WebApiConfig.cs di un tipico progetto ASP.NET Web API.
config.Routes.MapHttpBatchRoute( routeName: "batch", routeTemplate: "api/batch", batchHandler: new DefaultHttpBatchHandler(GlobalConfiguration.DefaultServer){ ExecutionOrder = BatchExecutionOrder.Sequential //indichiamo NonSequential se l'ordine sequenziale non ci interessa } );
Il DefaultHttpBatchHandler si occuperà di estrarre dal batch tutte le richieste HTTP e di reintrodurle, una ad una, nella pipeline di elaborazione del servizio, proprio come se fossero arrivate singolarmente. Successivamente, aggiungerà i loro risultati al contenuto di un'unica risposta.
Lato client, prepariamo la richiesta batch usando le classi del namespace System.Net.Http.
//Definiamo l'URL base del servizio ASP.NET Web API. string baseAddress = "http://localhost:56813/"; //Iniziamo col preparare le singole richieste HTTP da includere nel batch //In questo esempio, le prime due sono richieste POST per inviare dei dati... HttpRequestMessage inserisciTemperaturaRoma = new HttpRequestMessage( HttpMethod.Post, baseAddress + "api/temperature/roma") { Content = new ObjectContent<string>("15.5 °C", new JsonMediaTypeFormatter()) }; HttpRequestMessage inserisciTemperaturaMilano = new HttpRequestMessage( HttpMethod.Post, baseAddress + "api/temperature/milano") { Content = new ObjectContent<string>("12.7 °C", new JsonMediaTypeFormatter()) }; //...e la terza è una richiesta GET per ottenere un valore HttpRequestMessage leggiMediaItaliana = new HttpRequestMessage( HttpMethod.Get, baseAddress + "api/temperature/italia/media"); //Creiamo la richiesta batch vera e propria, assegnandole un contenuto //multipart/mixed. Il MultipartContent ci consente di aggiungere le 3 //richieste che abbiamo appena preparato HttpRequestMessage richiestaBatch = new HttpRequestMessage( HttpMethod.Post, baseAddress + "api/batch") { Content = new MultipartContent("mixed"){ new HttpMessageContent(inserisciTemperaturaRoma), new HttpMessageContent(inserisciTemperaturaMilano), new HttpMessageContent(leggiMediaItaliana) } }; //Finalmente inviamo la richiesta batch usando un'istanza di HttpClient HttpClient client = new HttpClient(); HttpResponseMessage rispostaBatch = client.SendAsync(richiestaBatch).Result; //Assicuriamoci che la richiesta batch abbia avuto successo rispostaBatch.EnsureSuccessStatusCode(); //Anche la risposta sarà di tipo multipart/mixed, ovvero conterrà //un insieme di contenuti IEnumerable<HttpContent> contenuti = rispostaBatch.Content .ReadAsMultipartAsync().Result.Contents; //Ognuno dei contenuti è l'HttpResponseMessage relativo ad una nostra //richiesta. In questo esempio, ci interessa esaminare solo l'ultima risposta, // quella relativa alla richiesta GET HttpResponseMessage rispostaTemperaturaMedia = contenuti.Last() .ReadAsHttpResponseMessageAsync().Result; //Il contenuto della risposta sarà una stringa JSON... string contenutoJson = rispostaTemperaturaMedia .Content.ReadAsStringAsync().Result; //...che deserializziamo in questo modo string temperaturaMedia = JsonConvert .DeserializeObject<string>(contenutoJson); //Il valore ottenuto possiamo usarlo in un messaggio da mostrare a video string messaggio = string.Format( "La media italiana è di {0}", temperaturaMedia);
Come si vede dall'esempio, siamo liberi di inserire nel batch varie richieste HTTP, siano esse GET, POST o di altro tipo.
L'interoperabilità è uno dei punti di forza di ASP.NET Web API, infatti l'utente non sarà in alcun modo costretto ad usare un client .NET ma potrà preparare la richiesta batch usando un linguaggio a sua scelta. L'importante è che il suo contenuto sia conforme alla specifica multipart/mixed (http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html), così come descritta dal W3C.
Ed ecco il contenuto grezzo della nostra richiesta batch: se lo esaminiamo, noteremo che ogni richiesta incapsulata al suo interno è stata ben isolata grazie ad un boundary che agisce da delimitatore.
POST http://localhost:56813/api/batch HTTP/1.1 Content-Type: multipart/mixed; boundary="bff10b16-941b-4128-b8c8-0e6589c475ed" Host: localhost:56813 Content-Length: 654 Expect: 100-continue Connection: Keep-Alive --bff10b16-941b-4128-b8c8-0e6589c475ed Content-Type: application/http; msgtype=request POST /api/temperature/roma HTTP/1.1 Host: localhost:56813 Content-Type: application/json; charset=utf-8 "15.5 °C" --bff10b16-941b-4128-b8c8-0e6589c475ed Content-Type: application/http; msgtype=request POST /api/temperature/milano HTTP/1.1 Host: localhost:56813 Content-Type: application/json; charset=utf-8 "12.7 °C" --bff10b16-941b-4128-b8c8-0e6589c475ed Content-Type: application/http; msgtype=request GET /api/temperature/italia/media HTTP/1.1 Host: localhost:56813 --bff10b16-941b-4128-b8c8-0e6589c475ed--
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Conoscere il rendering Server o WebAssembly a runtime in Blazor
Configurare e gestire sidecar container in Azure App Service
Esporre i propri servizi applicativi con Semantic Kernel e ASP.NET Web API
Selettore CSS :has() e i suoi casi d'uso avanzati
Utilizzare Copilot con Azure Cosmos DB
Potenziare la ricerca su Cosmos DB con Full Text Search
Creare una libreria CSS universale: Cards
Supportare lo HierarchyID di Sql Server in Entity Framework 8
Recuperare l'ultima versione di una release di GitHub
Fornire parametri ad un Web component HTML
Generare HTML a runtime a partire da un componente Razor in ASP.NET Core
Persistere la ChatHistory di Semantic Kernel in ASP.NET Core Web API per GPT
I più letti di oggi
- Eseguire query in contemporanea con EF
- Fissare una versione dell'agent nelle pipeline di Azure DevOps
- .NET Aspire per applicazioni distribuite
- Utilizzare Locust con Azure Load Testing
- Autenticazione di git tramite Microsoft Entra ID in Azure DevOps
- Repaint, Reflow e Compositing: Come Funziona il Rendering nel Browser
- ecco tutte le novità pubblicate sui nostri siti questa settimana: https://aspit.co/wkly buon week-end!
- ecco tutte le novità pubblicate sui nostri siti questa settimana: https://aspit.co/wkly buon week-end!