我无法将.NET Core API Controller端点解析为CSV下载。我使用以下从.NET 4.5控制器中提取的代码:
[HttpGet]
[Route("{id:int}")]
public async Task<HttpResponseMessage> Get(int id)
{
string csv = await reportManager.GetReport(CustomerId, id);
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(csv);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
response.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("attachment") { FileName = "report.csv" };
return response;
}
当我从Angular 4应用程序中点击此端点时,我会将以下响应写入浏览器:
{
"version": {
"major": 1,
"minor": 1,
"build": -1,
"revision": -1,
"majorRevision": -1,
"minorRevision": -1
},
"content": {
"headers": [
{
"key": "Content-Type",
"value": [
"text/csv"
]
},
{
"key": "Content-Disposition",
"value": [
"attachment; filename=11.csv"
]
}
]
},
"statusCode": 200,
"reasonPhrase": "OK",
"headers": [ ],
"requestMessage": null,
"isSuccessStatusCode": true
}
我的期望是当我点击端点时,系统会提示用户下载CSV。
我发现了this关于如何&#34; export&#34;的帖子.NET Core中的CSV。问题是我从它的源(AWS S3存储桶)中检索内存中的CSV,而此代码似乎仅在您有IEnumerable<object>
时才有效。
我想知道我的问题是否存在于请求或响应标头中,哪些内容阻止浏览器从我的API中检索CSV。这是我在浏览器控制台中看到的内容:
答案 0 :(得分:9)
FileResult
如果您希望客户获得&#34; 保存文件&#34;对话框。
此处有多种选择,例如FileContentResult
,FileStreamResult
,VirtualFileResult
,PhysicalFileResult
;但它们都来自FileResult
- 所以我们将在这个例子中使用那个。
public async Task<FileResult> Download()
{
string fileName = "foo.csv";
byte[] fileBytes = ... ;
return File(fileBytes, "text/csv", fileName); // this is the key!
}
如果您更喜欢使用
public async Task<IActionResult>
,则上述内容也适用。关键是您返回
File
类型。
FileResult
会自动为Content-Disposition
提供正确的attachment
标题。
如果要在浏览器中打开文件(&#34;内联&#34;),而不是提示&#34;保存文件&#34;对话框(&#34;附件&#34;)。然后,您可以通过更改Content-Disposition
标头值来完成此操作。
例如,我们想在浏览器中显示PDF
文件。
public IActionResult Index()
{
byte[] contents = FetchPdfBytes();
Response.AddHeader("Content-Disposition", "inline; filename=test.pdf");
return File(contents, "application/pdf");
}
归功于此SO Answer
自定义格式化程序通常是一个很好的选择,因为它允许客户端询问他们想要数据的类型,例如更流行的JSON或不太流行的XML。
这主要通过提供客户端传递给服务器的Accept
标头中指定的内容来实现,例如CSV,XLS,XML,JSON等。
由于您使用的格式类型为"text/csv"
- 目前,当您使用预先捆绑的选项(例如AddMvc()
)时,没有输入/输出格式化程序...所以,你必须加入,如下:
services.AddMvc(options =>
{
options.InputFormatters.Insert(0, new MyCustomInputFormatter());
options.OutputFormatters.Insert(0, new MyCustomOutputFormatter());
});
以下是自定义格式化程序的一个非常简单的版本,它是随Microsoft Docs example一起提供的精简版本。
//using Microsoft.AspNetCore.Mvc.Formatters;
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);
}
}
// 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()
{
}
}
有关详细信息,我建议您阅读:
答案 1 :(得分:4)
强制下载文件而不是内联显示的正确方法是使用Content-Disposition
response header.虽然以下解决方案有效(see documentation),但有人指出这可能是无意的副作用。
将Content-Type
响应标头设置为application/octet-stream
会强制大多数主流浏览器提示用户保存文件,而不是在窗口中显示该文件。
尝试做这样的事情:
var result = new FileContentResult(myCsvByteArray, "application/octet-stream");
result.FileDownloadName = "my-csv-file.csv";
return result;