Restituire CSV da WebAPI con un MediaTypeFormatter personalizzato

di Moreno Gentili, in ASP.NET Web API,

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

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi