我正在开发一个应用程序,它为用户提供一些下载文件的链接。该页面本身由 MVC 控制器提供,但链接指向在单独域上运行的 WebAPI 控制器。
(我更喜欢同一个域,但由于各种原因,它必须是一个单独的项目,它将在一个单独的域上运行。我不认为 CORS 是问题的一部分,因为这不使用 XHR,但我提到了它以防万一)。
所以在开发中,主要的MVC项目是http://localhost:56626/Reports/
页面上的链接可能如下所示:
<a href="http://localhost:51288/ReportDownload?ID=12345">Report 12345</a>
其中端口 51288 托管 Web API。
WebAPI 控制器使用 ReportID 定位文件,并将其内容写入响应流,将处置设置为附件:
//security.permission checks and scaffolding/database interaction
//left out for clarity
try
{
string filename = @"C:\ReportFiles\TestReport.csv";
var stream = new FileStream(path, FileMode.Open);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
var disp = new ContentDispositionHeaderValue("attachment");
disp.FileName = "TestReport.csv";
result.Content.Headers.ContentDisposition = disp;
return result;
}
catch(Exception ex)
{
//how to return a response that won't redirect on error?
}
通过这样做,用户可以单击链接,无需任何重定向,系统会提示用户保存或打开文件,这正是我想要的;他们留在带有链接的原始页面上,只是从浏览器中获得一个打开/保存对话框。
当 Web API 控制器出现问题时,就会出现问题 - 异常或某些内部逻辑条件意味着无法下载文件。
在这种情况下,当单击链接时,不会发生下载(显然),而是将它们带到目标 URL,即
http://localhost:51288/api/ReportDownload?ReportID=12345
这不符合我的要求。我更希望能够通过在响应中返回例如 HTTP 500 以某种方式在客户端捕获错误,并向用户显示下载失败的消息。
现在说实话,我什至不明白浏览器如何首先执行“就地”文件/保存对话框:
我一直认为,如果您单击没有显式
target
属性的链接,浏览器只会在当前选项卡中打开新请求,即它只是对目标 URL 的另一个 GET 请求,但似乎情况并非如此在这种情况下,浏览器似乎正在对目标 URL 进行隐藏的后台获取(在 FF、Chrome 和 IE 中的行为相同),我什至在 F12 工具中都看不到。
F12 网络日志根本没有显示任何活动,除非在响应尚未设置为
Content-Disposition: attachment
错误的特定情况下 - 只有在这种情况下,我才能看到(失败的)HTTP GET 记录在网络请求列表中。
我想我可以在控制器中捕获任何异常并发送回一个名为“Error.csv”的虚拟文件,内容为“哈哈,不!” 或类似的东西,但那将是最后的手段......欢迎任何想法!