Una delle libertà che ASP.NET Web API ci concede, è quella di scrivere action che restituiscano oggetti di un tipo arbitrario, che verranno poi serializzati in XML o JSON da uno dei media formatter disponibili.
Nonostante questo ci renda più produttivi, ci sono situazioni in cui preferiamo mantenere un controllo più diretto su ciò che viene inviato al client. Impostando un HttpResponseMessage come valore di ritorno di un'action, riusciremo a riguadagnare il controllo su ciascuna delle parti che compongono la Response, ovvero il suo status code, le intestazioni e, ovviamente, il suo contenuto.
Assegnando un'istanza di StreamContent al contenuto di un HttpResponseMessage, riusciremo ad inviare dati in modalità streaming al client. Il grosso vantaggio, in questo caso, è che riusciremo a gestire grandi quantità di dati senza che debbano essere preventivamente caricate in memoria.
Vediamo dunque come assemblare un tale HttpResponseMessage.
//In questo esempio, l'action ha lo scopo di inviare un file binario //al client, in streaming. Il valore di ritorno di questa action è, per //l'appunto, un HttpResponseMessage public HttpResponseMessage GetDocumento(int idDocumento) { //Creo una risposta con lo status code OK (200) HttpResponseMessage risposta = Request.CreateResponse(HttpStatusCode.OK); //Imposto il content type del file binario risposta.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octect-stream"); //Invoco il metodo che restituisce il percorso fisico di un file a partire //dal suo Id string percorsoFile = OttieniPercorsoFileDaIdDocumento(idDocumento); //Apro uno FileStream in lettura indicando il percorso fisico del file FileStream stream = File.OpenRead(percorsoFile); //Lo assegno al contenuto della risposta, incapsulandolo //in uno StreamContent risposta.Content = new StreamContent(stream); //Restituisco l'HttpResponseMessage, che attraverserà la pipeline //di ASP.NET Web API prima di giungere al client return risposta; }
L'esempio resta valido con qualsiasi tipo Stream, sia che attinga da risorse residenti nel file system che da altre sorgenti come la rete o la memoria del server. Possiamo addirittura essere noi stessi a decidere con quali modalità e tempi inviare dati al client. La classe PushStreamContent ci permette infatti di fornire un delegato che rappresenterà la nostra strategia di scrittura sullo Stream di risposta. Per esempio, può tornarci utile per realizzare il long polling, ovvero per temporeggiare in attesa che una certa quantità di dati si sia resa disponibile per il trasferimento.
//In questo esempio, l'action ha lo scopo di tenere il client in attesa //finché non avrà sufficienti dati da scrivere sullo stream public HttpResponseMessage GetDati() { //Creo una risposta impostando lo status code 200 (OK) var risposta = Request.CreateResponse(HttpStatusCode.OK); //Imposto il content type risposta.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octect-stream"); //Creo un PushStreamContent, che mi lascia decidere cosa //e quando scrivere sullo Stream. Essendo un'operazione di lunga //durata, la rendo asincrona con la parola chiave async risposta.Content = new PushStreamContent(async (stream, content, context) => { //Continuo ad attendere finché non arrivano dei dati //(attenzione a non incappare in un timeout) while(!DatiDisponibili()) { await Task.Delay(5000); } //Finalmente ottengo i dati e li scrivo sullo Stream byte[] dati = OttieniDati(); await stream.WriteAsync(dati, 0, dati.Length); stream.Dispose(); }); return risposta; }
Alcuni tipi di Stream e di strategia impediscono al server di conoscere anticipatamente l'esatta dimensione in bytes della Response. In questo caso, il valore di Content-Length non potrà essere calcolato e, al suo posto, verrà aggiunta l'intestazione Transfer-Encoding: chunked. Questa è l'indicazione per il client che il contenuto della Response verrà trasferito in frammenti discreti.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Effettuare il binding di date in Blazor
Utilizzare il nuovo modello GPT-4o con Azure OpenAI
Utilizzare i primary constructor di C# per inizializzare le proprietà
Migliorare i tempi di risposta di GPT tramite lo streaming endpoint in ASP.NET Core
Utilizzare Tailwind CSS all'interno di React: installazione
Hosting di componenti WebAssembly in un'applicazione Blazor static
Eseguire le GitHub Actions offline
Registrare servizi multipli tramite chiavi in ASP.NET Core 8
Paginare i risultati con QuickGrid in Blazor
Creare una custom property in GitHub
Migrare una service connection a workload identity federation in Azure DevOps
Evitare (o ridurre) il repo-jacking sulle GitHub Actions
I più letti di oggi
- Effettuare il log delle chiamate a function di GPT in ASP.NET Web API
- ecco tutte le novità pubblicate sui nostri siti questa settimana: https://aspit.co/wkly buon week-end!
- Utilizzare il metodo CountBy di LINQ per semplificare raggruppamenti e i conteggi
- Creare una libreria CSS universale: Cards
- Eseguire script pre e post esecuzione di un workflow di GitHub