Web Api不会使用jQuery Ajax和Basic Auth下载文件

我正在使用ASP.NET Web API构建Web服务(和站点)的原型,该原型具有下载文件的方法。 当前端的用户按下导出按钮时,控制器发出并接收jQuery ajax GET请求,然后控制器调用名为Excel的方法(如下所示)。 该方法运行没有任何问题并完成。 当我在标题中查看Chrome时(请参阅https://skydrive.live.com/redir?resid=2D85E5C937AC2BF9!77093 ),它会收到响应(据我所知)所有正确的标题。

我正在使用Basic Auth,因此使用http授权头传输用户凭据,我已使用Ajax选项手动将其添加到每个jQuery Ajax请求中。

var excelRequest = $.ajax({ url: 'http://localhost:59390/api/mycontroller/excel', cache: false, type: 'GET', data: gridString, dataType: 'json', contentType: 'application/json; charset=utf-8' }); $.ajaxSetup({ beforeSend: function (xhr) { SetAuthRequestHeader(xhr) } }); function SetAuthRequestHeader(jqXHR) { var usr = "Gebruiker2"; // TODO: Change, this is for testing only. var pw = "Wachtwoord23"; jqXHR.setRequestHeader("Authorization", "Basic " + Base64.encode(usr + ":" + pw)); } 

我的原型有一些我应该提到的特征:

  • 在Authorization标头中使用Basic Auth

  • Web服务和调用该服务的网站位于不同的域中,因此我使用CORS并将以下内容添加到web.config中

     <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS"   

下面显示的是整个Excel方法。

  [HttpGet] // Get api/myController/excel public HttpResponseMessage Excel(string sidx, string sord, int page, int rows, string Depot, string PDID, string User, string Property, string Value) { if (AuthHelper.AuthService.HasValidCredentials(Request)) { var gridResult = this.GetDataJqGridFormat( sidx, sord, page, rows, Depot, PDID, User, Property, Value); // Generate a HTML table. StringBuilder builder = new StringBuilder(); // We create a html table: builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); // Create response from anonymous type foreach (var item in gridResult.rows) { builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); } builder.Append("
DEPOTPDIDUSERPROPERTYVALUE
" + item.cell[0] + "" + item.cell[2] + "" + item.cell[3] + "" + item.cell[4] + "" + item.cell[5] + "
"); HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); result.Content = new StringContent(builder.ToString()); result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); result.Content.Headers.ContentDisposition.FileName = "file.xls"; return result; } else { throw ForbiddenResponseMessage(); } }

这是标题,也应该返回文件: https : //skydrive.live.com/redir?resid = 2D85E5C937AC2BF9!77093

我想要的是在我调用指向excel方法的url时下载文件。 我不明白为什么它不会下载。 甚至可以这样下载文件吗?

好吧我明白了。 为了免除你的阅读,我不可能按照我想要的方式去做。

首先,我无法使用jQuery Ajax下载该文件。 正如我所预料的(或担心),无法使用Ajax下载文件。 这是出于安全考虑。 请参阅为什么使用ajax请求无法下载文件?

但我仍然需要下载该文件。 有了上述知识,解决方案/解决方案似乎很简单。

我将执行ajax调用的javascript函数更改为以下内容:

  var gridInfo = { sidx: "", sord: "asc", nd: 1371046879480, rows: 50, page: 1 } var payLoad = "?" + PrepareSearchQueryString() + "&" + serialize(gridInfo); window.location= "http://site:59390/api/mycontroller/excel" + payLoad 

但这只涉及部分问题,因为我需要设置授权标头。

感谢Stackoverflow用户SLaks回答了我的相关问题( 使用javascript设置标题 )我能够找到用户名和密码看起来像https:// user:password@domain.com/path?query的url可能是一个解决方案。 可悲的是,它没有用。 它不被IE接受,在Chrome中使用它没有找到授权标题。

由于我也在使用jqGrid,我可以使用jqGrid设置授权头。 在网站上使用“搜索”function时,此function完全正常。 我的Javascript导出function现在看起来像这样:

  var sUrl = "http://localhost:59390/api/Wmssettings/excel" + "?" + PrepareSearchQueryString() ; $("#gridOverview").jqGrid('excelExport', { url: sUrl }); 

但是看看请求我注意到,与使用搜索function时不同,没有传递授权标头。 我找到了原因。 在查看jqGrid源时,我注意到Grid正在执行window.location以指向下载位置。 当这样做时,没有办法传递基本的身份validation信息。

所以我走的唯一方法就是我试图避免的方式。 我更改了我的控制器方法以返回包含指向该文件的URL的json,然后我使用javascript重定向到名为downloadcontroller的新控制器。

控制器Excel方法

  [HttpGet] // Get api/wmssettings/excel public HttpResponseMessage Excel(string sidx, string sord, int page, int rows, string Depot, string PDID, string User, string Property, string Value) { if (AuthHelper.AuthService.HasValidCredentials(Request)) { var gridResult = this.GetDataJqGridFormat(sidx, sord, page, rows, Depot, PDID, User, Property, Value); // Generate a HTML table. StringBuilder builder = new StringBuilder(); // We create a html table: builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); // Create response from anonymous type foreach (var item in gridResult.rows) { builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(""); } builder.Append("
DEPOTPDIDUSERPROPERTYVALUE
" + item.cell[0] + "" + item.cell[2] + "" + item.cell[3] + "" + item.cell[4] + "" + item.cell[5] + "
"); // Put all in a file and return the url: string fileName = "export" + "_" + Guid.NewGuid().ToString() + ".xls"; using (StreamWriter writer = new StreamWriter(HttpContext.Current.Server.MapPath("~/Downloads" + fileName))) { writer.Write(builder.ToString()); writer.Flush(); } HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK, fileName); return result; } else { throw ForbiddenResponseMessage(); } }

JavaScript导出方法

  var gridParams = { //"nd": Math.floor((Math.random() * 10000000) + 1), sidx: "", sord: "asc", page: "1", rows: "50" } payLoad = PrepareSearchQueryString() + "&" + serialize(gridParams); var excelRequest = $.ajax({ url: 'http://localhost:59390/api/Wmssettings/excel', cache: false, type: 'GET', data: payLoad, dataType: 'json', contentType: 'application/json; charset=utf-8' }); excelRequest.success(function (data, code, jqXHR) { if (data == null) { alert('sd'); ShowErrorMessage("There was no file created.", "", "Title"); } else { window.location = 'http://localhost:59390/api/download/?fileName=' + data; } }); 

我在WebApiConfig.cs中添加了以下行

  config.Routes.MapHttpRoute("Downloadcontroller", "api/{controller}/{action}", new { action = "Get"}, new { httpMethod = new HttpMethodConstraint(allowedVerbsGet), controller="download"}); 

最后下载控制器:

  public class DownloadController : ApiController { [HttpGet] public HttpResponseMessage Get(string fileName) { HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); string fileLocation = HttpContext.Current.Server.MapPath("~/Downloads" + fileName); if (!File.Exists(fileLocation)) { throw new HttpResponseException(HttpStatusCode.OK); } Stream fileStream = File.Open(fileLocation, FileMode.Open); result.Content = new StreamContent(fileStream); result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-excel"); result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); return result; } } 

可以得出以下结论:

  • 使用Ajax下载文件是不可能的,而是我必须重定向到或精确的文件位置或另一个控制器。

  • 在IE中不允许将用户名和密码放在url中,并且似乎在其他浏览器中出现问题。 例如,在Chrome中,授权标头保持为空。

  • 在执行window.location时,无法传递其他标头,例如基本身份validation所需的授权标头。

那就是它。 答案是,我想要做的事情不可能以我喜欢的方式进行。 它现在工作正常。