Returning CSV from .NET Core controller

Newcomers to this question please see Svek's answer. The original question is concerning http Content-Disposition, but it looks like search engines send generic .net core csv queries here. Svek's answer provides a good overview of the tools available to .Net Core for returning CSV data from a controller.


The proper way to force a file to be downloaded instead of displayed inline is using the Content-Disposition response header. While the below solution works (see documentation) it's been pointed out that this can have unintended side effects.


Old Answer

Setting the Content-Type response header to application/octet-stream will force most major browsers to prompt the user to save the file instead of displaying it in the window.

Try doing something like this:

var result = new FileContentResult(myCsvByteArray, "application/octet-stream");
result.FileDownloadName = "my-csv-file.csv";
return result;

See my answer to this similar question for more info


Solution: Use FileResult

This should be used if you want the client to get the "Save File" dialog box.

There are a variety to choose from here, such as FileContentResult, FileStreamResult, VirtualFileResult, PhysicalFileResult; but they all derive from FileResult - so we will go with that one for this example.

public async Task<FileResult> Download()
{
    string fileName = "foo.csv";
    byte[] fileBytes = ... ;

    return File(fileBytes, "text/csv", fileName); // this is the key!
}

The above will also work if you use public async Task<IActionResult> if you prefer using that instead.

The key is that you return a File type.

Extra: Content-Disposition

The FileResult will automatically provide the proper Content-Disposition header to attachment.

If you want to open the file in the browser ("inline"), instead of prompting the "Save File" dialog ("attachment"). Then you can do that by changing the Content-Disposition header value.

Take for example, we want to show the PDF file in the browser.

public IActionResult Index()
{
    byte[] contents = FetchPdfBytes();
    Response.AddHeader("Content-Disposition", "inline; filename=test.pdf");
    return File(contents, "application/pdf");
}

Credit to this SO Answer


Custom Formatters

Custom formatters are a great choice in general, because they allow the client to ask for the type they want the data as, such as the more popular JSON or the less popular XML.

This primarily works by serving the content as specified in the Accept header that the client passes to the server, such as CSV, XLS, XML, JSON, etc.

You want to use a format type of "text/csv" but there is no predefined formatter for this, so you will have to manually add it to the input and output formatter collections:

services.AddMvc(options =>
{
    options.InputFormatters.Insert(0, new MyCustomInputFormatter());
    options.OutputFormatters.Insert(0, new MyCustomOutputFormatter());
});

Very Simple Custom Formatter

Here's a very simple version of a custom formatter, which is a stripped-down version that was provided with the Microsoft Docs example.

public class CsvOutputFormatter : TextOutputFormatter
{
    public CsvOutputFormatter()
    {
        SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/csv"));
        SupportedEncodings.Add(Encoding.UTF8);
        SupportedEncodings.Add(Encoding.Unicode);
    }

    protected override bool CanWriteType(Type type)
    {
        return true; // you could be fancy here but this gets the job done.
    }

    public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
    {
        var response = context.HttpContext.Response;
        
        // your magic goes here
        string foo = "Hello World!";

        return response.WriteAsync(foo);
    }
}

Forcing a Particular Format

// force all actions in the controller
[Produces("text/csv")]
public class FooController
{
    // or apply on to a single action
    [Produces("text/csv")]
    public async Task<IActionResult> Index()
    {
    }
}  

For more information, I would recommend that you read:

  • Introduction to formatting response data in ASP.NET Core MVC
  • Custom formatters in ASP.NET Core MVC