.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をダウンロードするように求められることです。
.NET CoreでCSVを「エクスポート」する方法に関する this の投稿を見つけました。問題は、ソース(AWS S3バケット)からCSVインメモリを取得しているのに対して、このコードはIEnumerable<object>
。
ブラウザーがAPIからCSVを取得するのを妨げる何かがある、リクエストまたはレスポンスヘッダーに問題があるのではないかと思っています。ブラウザコンソールに表示される内容は次のとおりです。
表示されるインラインの代わりにファイルを強制的にダウンロードする適切な方法は、 Content-Disposition
応答ヘッダーを使用することです。 以下の解決策は機能します( ドキュメントを参照してください ).
Content-Type
応答ヘッダーをapplication/octet-stream
に設定すると、ほとんどの主要なブラウザーは、ファイルをウィンドウに表示する代わりに保存するようユーザーに要求します。
次のようなことを試してください:
var result = new FileContentResult(myCsvByteArray, "application/octet-stream");
result.FileDownloadName = "my-csv-file.csv";
return result;
FileResult
を使用しますこれは、クライアントに「Save File」ダイアログボックスを取得させる場合に使用する必要があります。
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>
を使用する場合も上記は機能します。重要な点は、a
File
type。
FileResult
は、attachment
に適切なContent-Disposition
ヘッダーを自動的に提供します。
「ファイルを保存」ダイアログ(「添付ファイル」)の代わりに、ブラウザでファイルを開きたい場合(「インライン」)。その後、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など、データが必要なタイプを要求できるためです。
これは主に、CSV、XLS、XML、JSONなど、クライアントがサーバーに渡すAccept
ヘッダーで指定されたコンテンツを提供することで機能します。
"text/csv"
のフォーマットタイプを使用したいが、このための事前に定義されたフォーマッターがないため、入力および出力フォーマッターコレクションに手動で追加する必要があります。
services.AddMvc(options =>
{
options.InputFormatters.Insert(0, new MyCustomInputFormatter());
options.OutputFormatters.Insert(0, new MyCustomOutputFormatter());
});
これは、カスタムフォーマッターの非常に単純なバージョンです。これは、 Microsoft Docsの例 で提供された、簡略化されたバージョンです。
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()
{
}
}
詳細については、以下を読むことをお勧めします。