通过Web Api控制器下载文件 - 处理错误

时间:2013-10-16 11:05:40

标签: c# asp.net http asp.net-web-api

我正在开发一个应用程序,它为用户提供了一些下载文件的链接。 页面本身由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控制器出现问题时出现问题 - 异常或某些内部逻辑条件意味着无法下载文件。

  • 在这种情况下,点击链接时,显然不会发生下载,而是将它们转到目标网址,即http://localhost:51288/api/ReportDownload?ReportID=12345,这对我的要求是不可取的。

  • 我宁愿能够在客户端以某种方式捕获错误,例如返回响应中的HTTP 500,只向用户显示下载失败的消息。

说实话,我甚至不了解浏览器如何首先在“就地”文件/保存对话框中执行:

  • 我一直认为如果点击没有明确target属性的链接,浏览器就会在当前标签中打开新请求,即它只是对目标网址的另一个GET请求,但它似乎情况并非如此

  • 在这种情况下,浏览器似乎正在对目标网址进行隐藏的背景提取(在FF,Chrome和IE中也是如此),我甚至无法在F12工具中看到这些内容。

  • F12网络日志完全没有显示任何活动,除非在没有将响应设置为Content-Disposition: attachment的特定情况下,即错误 - 在这种情况下我只能看到(失败的)HTTP GET正在登录网络请求列表。

我想我可以在控制器中捕获任何异常并发回一个名为“Error.csv”的虚拟文件,内容为“Ha Ha Nope!”或类似的东西,但这将是最后的手段...欢迎任何想法!

1 个答案:

答案 0 :(得分:4)

如果用户点击链接,浏览器将跟随它 - 然后根据响应标题和浏览器配置,它将显示文件对话框或直接呈现 - 您无法真正改变该行为(除了单击链接时使用preventDefault,这种目的失败了。

我建议您仔细查看http://jqueryfiledownload.apphb.com/,它可以让您执行以下操作:

$.fileDownload('some/file/url')
    .done(function () { alert('File download a success!'); })
    .fail(function () { alert('File download failed!'); });

然后你可以使用jQuery绑定下载操作。