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