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
Eseguire un metodo asincrono dopo il set di una proprietà in Blazor 8
Creare un'applicazione React e configurare Tailwind CSS
Aprire una finestra di dialogo per selezionare una directory in WPF e .NET 8
Filtering sulle colonne in una QuickGrid di Blazor
Creare una custom property in GitHub
Gestire il colore CSS con HWB
Effettuare il log delle chiamate a function di GPT in ASP.NET Web API
Sostituire la GitHub Action di login su private registry
Bloccare l'esecuzione di un pod in mancanza di un'artifact attestation di GitHub
Ottimizzare le pull con Artifact Cache di Azure Container Registry
Migliorare l'organizzazione delle risorse con Azure Policy
Utilizzare la funzione EF.Parameter per forzare la parametrizzazione di una costante con Entity Framework