Web Api won't下载文件使用jQuery Ajax和基本授权

本文关键字:Ajax jQuery 授权 文件 won Api 下载 Web | 更新日期: 2023-09-27 18:18:05

我正在使用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));
}

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

  • 在授权头中使用基本认证

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

<httpProtocol>
    <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="access-control-allow-headers" value="Content-Type, Authorization, Accept" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" 
    </customHeaders>
</httpProtocol>

下面是整个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("<table border=1>");
            builder.Append("<tr><td>DEPOT</td>");
            builder.Append("<td>PDID</td>");
            builder.Append("<td>USER</td>");
            builder.Append("<td>PROPERTY</td>");
            builder.Append("<td>VALUE</td></tr>");
            // Create response from anonymous type            
            foreach (var item in gridResult.rows)
            {
                builder.Append("</tr>");
                builder.Append("<tr>");
                builder.Append("<td>" + item.cell[0] + "</td>");
                builder.Append("<td>" + item.cell[2] + "</td>");
                builder.Append("<td>" + item.cell[3] + "</td>");
                builder.Append("<td>" + item.cell[4] + "</td>");
                builder.Append("<td>" + item.cell[5] + "</td>");
            }
            builder.Append("</table>");
            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。我不明白为什么不能下载。用这种方式下载文件可能吗?

Web Api won't下载文件使用jQuery Ajax和基本授权

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

首先,我无法使用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设置授权头。当使用网站上的搜索功能时,这工作得很好。我的Javascript Export函数现在看起来像这样:

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

但是查看请求时,我注意到与使用Search功能不同,没有传递授权头。我确实找到了原因。在查看jqGrid源代码时,我注意到Grid只是在做一个窗口。Location指向下载位置。当这样做的时候没有办法传递基本的认证信息。

所以我唯一要走的路,就是我想要避开的路。我改变了我的控制器方法,返回一个包含指向文件的url的json,然后我使用javascript重定向到一个名为downloadcontroller的新控制器。

Controller Excel Method

        [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("<table border=1>");
            builder.Append("<tr><td>DEPOT</td>");
            builder.Append("<td>PDID</td>");
            builder.Append("<td>USER</td>");
            builder.Append("<td>PROPERTY</td>");
            builder.Append("<td>VALUE</td></tr>");
            // Create response from anonymous type            
            foreach (var item in gridResult.rows)
            {
                builder.Append("</tr>");
                builder.Append("<tr>");
                builder.Append("<td>" + item.cell[0] + "</td>");
                builder.Append("<td>" + item.cell[2] + "</td>");
                builder.Append("<td>" + item.cell[3] + "</td>");
                builder.Append("<td>" + item.cell[4] + "</td>");
                builder.Append("<td>" + item.cell[5] + "</td>");
            }
            builder.Append("</table>");
            // 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中,授权头保持空

  • 处理窗口时。

好了,就是这样。答案是,我想做的事情无法以我喜欢的方式实现。