Tra le funzionalità di maggior pregio di ASP.NET WebAPI, vi è l'implementazione della cosiddetta content negotiation, che permette ad un client di indicare un formato preferito nell'ottenere dati dal server. Inviando una richiesta HTTP con l'intestazione Accept valorizzata su application/json o application/xml, il client potrà così chiedere di ottenere contenuto JSON o XML, a sua discrezione.
Se si rendesse necessario supportare altri formati, come ad esempio CSV o particolari tracciati testuali, ci basterà scrivere un MediaTypeFormatter personalizzato, ovvero quel componente della pipeline di ASP.NET WebAPI che si occupa di serializzare i nostri oggetti nel formato desiderato (o, viceversa, di deserializzare oggetti CLR da una richiesta).
Dunque aggiungiamo una nuova classe al nostro progetto (ad esempio in una cartella /Formatters) e facciamola derivare da una delle due classi astratte che troviamo nel namespace System.Net.Http.Formatting:
- MediaTypeFormatter è il tipo da cui dobbiamo derivare se vogliamo sfruttare appieno il pattern asincrono basato sui Task. Utile in casi particolari, quando la serializzazione può prolungarsi per un certo periodo di tempo a causa di frequenti letture o scritture su risorse di I/O quali rete, disco o database.
- BufferedMediaTypeFormatter è forse più immediata da utilizzare, dato che nasconde il pattern asincrono usato dal MediaTypeFormatter dietro i suoi metodi sincroni. È il tipo base che useremo in questo esempio.
Ecco dunque come si presenta il nostro MediaTypeFormatter personalizzato, che progettiamo allo scopo di serializzare elenchi di oggetti nel formato CSV.
public class CsvMediaTypeFormatter : BufferedMediaTypeFormatter { public CsvMediaTypeFormatter() { // Dal costruttore aggiungiamo i media types supportati SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv")); SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/csv")); } public override bool CanWriteType(Type type) { //Dichiariamo quali sono i tipi di oggetti che questo //MediaTypeFormatter riuscirà a formattare //Restituiamo true quando l'action ci fornisce un elenco //generico di oggetti, come IEnumerable<T> return typeof(IEnumerable).IsAssignableFrom(type) && type.IsGenericType; } public override bool CanReadType(Type type) { //Un MediaTypeFormatter può anche essere impiegato //per deserializzare oggetti da una richiesta HTTP //In questo esempio restituiamo false, perché non //vogliamo supportare questo scenario return false; } public override void WriteToStream(Type type, object value, Stream writeStream, HttpContent content) { //TODO: qui scriveremo i dati in formato CSV sullo Stream della risposta } }
Ora che abbiamo visto i membri essenziali di un MediaTypeFormatter, passiamo ad implementare la logica di formattazione CSV. Per semplificare questo compito, possiamo avvalerci del pacchetto CsvHelper, scaricabile da NuGet. Dunque digitiamo il comando di installazione nella Package manager console di Visual Studio:
Install-Package CsvHelper
Finalmente abbiamo tutto ciò che ci serve per completare l'implementazione del metodo WriteToStream.
public override void WriteToStream(Type type, object value, Stream writeStream, HttpContent content) { using (var streamWriter = new StreamWriter(writeStream)) { using (var csvWriter = new CsvHelper.CsvWriter(streamWriter)) { //Scriviamo i nomi dei campi come prima riga dell'output CSV csvWriter.WriteHeader(type.GetGenericArguments().First()); //Poi forniamo l'elenco degli oggetti al metodo WriteRecords csvWriter.WriteRecords(value as IEnumerable); } } }
Non resta che aggiungere il MediaTypeFormatter alla configurazione della nostra WebAPI, così che vada ad affiancarsi ai già supportati JsonMediaTypeFormatter ed XmlMediaTypeFormatter.
Dunque apriamo il file /App_Start/WebApiConfig.cs e digitiamo:
config.Formatters.Add(new CsvMediaTypeFormatter());
A questo punto possiamo verificare il corretto funzionamento del nostro CsvMediaTypeFormatter. Rivolgiamo una richiesta HTTP ad una nostra action che restituisca un elenco di oggetti, ricordandoci di includere l'intestazione Accept: application/csv.
GET /api/catalogo HTTP/1.1 Host: localhost Accept: application/csv
Come previsto, ASP.NET WebAPI rispetta la preferenza che abbiamo espresso con l'intestazione Accept, e restituisce l'output CSV prodotto dal nostro CsvMediaTypeFormatter.
HTTP/1.1 200 OK Content-Type: application/csv Server: Microsoft-IIS/8.0 Content-Length: 228 Codice,Descrizione,PrezzoInEuro,Quantita,DisponibileDal 1000,Tavolino 4 posti,"90,00",0,31/10/2014 00:00:00 1001,"Sedia ""Ortensia"" in legno","20,00",0, 1002,Divano relax in ecopelle,"320,00",0,01/10/2014 00:00:00
E' importante apprezzare la modularità di ASP.NET WebAPI, che ci ha consentito di lasciare completamente inalterata la logica applicativa presente nei nostri ApiController. Grazie ad un MediaTypeFormatter personalizzato, infatti, siamo riusciti ad aggiungere il supporto ad un nuovo formato senza dover creare nuove action o modificare le action già esistenti.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Evitare (o ridurre) il repo-jacking sulle GitHub Actions
Visualizzare le change sul plan di Terraform tramite le GitHub Actions
Ottimizzare il mapping di liste di tipi semplici con Entity Framework Core
Limitare le richieste lato server con l'interactive routing di Blazor 8
Code scanning e advanced security con Azure DevOps
Evitare il flickering dei componenti nel prerender di Blazor 8
Usare le navigation property in QuickGrid di Blazor
Eseguire query verso tipi non mappati in Entity Framework Core
Migliorare l'organizzazione delle risorse con Azure Policy
Miglioramenti agli screen reader e al contrasto in Angular
Utilizzare i primary constructor in C#
Esporre i propri servizi applicativi con Semantic Kernel e ASP.NET Web API