Dienstag, 24. Oktober 2017

.NET Core Web API: Returning a file from an OutputFormatter

Let's say you want to return a csv file from your web API. To return a file from a controller action is as easy as writing

return File(stream, "text/csv");

It is not that eays if you are using an output formatter to be able to return either json or a file from the same controller method based on the Accept header. Then your controller looks like this:

return Ok(data);

And you need to register your output formatter with MVC:

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services
        .AddMvc(options =>
            {
                options.RespectBrowserAcceptHeader = true;
                options.OutputFormatters.Add(new CsvOutputFormatter());
            });
};

Within your formatter you need to render your data to Csv. You can easily use a NuGet package for this purpose. Writing the formatter is easy then:

public class ApetitoArticleCsvOutputFormatter : OutputFormatter
{

    public ApetitoArticleCsvOutputFormatter()
    {
        ContentType = "text/csv";
        SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/csv"));
    }
    
    public string ContentType { get; private set; }

    public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context)
    {

        var articles = context.Object as IEnumerable<Article>;

        var response = context.HttpContext.Response;
        response.Headers.Add("Content-Disposition", "attachment; filename=export.csv");

        using (var writer = context.WriterFactory(response.Body, Encoding.UTF8))
        {
            var csv = new CsvWriter(writer);
            csv.WriteRecords(articles);

            await writer.FlushAsync();

            
        }
    }
    
}


You just get your data from context.Object, get the Repsones object and a writer to the body stream and let CsvHelper write your data to this stream. Usually you will get the data being send back as text, for example when using swagger. But we want to make the browser save a file from the csv we requested.

The bold line does the trick: simply add a Content-Disposition header with value attachment and the name of you file. Now the browser saves the file, or swagger shows you a link to start the download.