使用jQuery Ajax和基本身份验证的Web API将无法下载文件 [英] Web Api won't download file using jQuery Ajax and Basic Auth

查看:311
本文介绍了使用jQuery Ajax和基本身份验证的Web API将无法下载文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用ASP.NET Web API来建立一个原型的Web服务(与本站)的具有下载文件的方法。当在前端$ P $用户psses导出按钮jQuery的AJAX GET请求是由控制器,它的依次调用名为Excel中的方法发出和接收的(如下图所示)。该方法运行没有任何问题和完成。当我在Chrome看标题(请参见 https://skydrive.live.com/redir ?渣油= 2D85E5C937AC2BF9!77093 ),它接收的响应(据我所关注的)所有正确的头。

我使用的基本认证,所以用户凭据所使用的我手动添加使用Ajax选项每jQuery的Ajax请求的HTTP授权头中传输。

  VAR excelRequest = $阿贾克斯({
    网址:的http://本地主机:59390 / API / myController的/ Excel的,
    缓存:假的,
    输入:GET,
    数据:gridString,
    数据类型:JSON,
    的contentType:应用/ JSON的;字符集= UTF-8
});$ .ajaxSetup({
    beforeSend:功能(XHR){
        SetAuthRequestHeader(XHR)
    }
});功能SetAuthRequestHeader(jqXHR){
    VAR USR =Gebruiker2; // TODO:更改,这是唯一的测试。
    VAR PW =Wachtwoord23;
    jqXHR.setRequestHeader(授权,基本+ Base64.en code(USR +:+ PW));
}

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


  • 使用基本身份验证在授权头


  • Web服务和Web网站,要求在不同的领域,所以我用CORS并添加以下到web.config

  • 服务

 < httpProtocol>
    < customHeaders>
        <添加名称=访问控制允许来源VALUE =*/>
        <添加名称=访问控制 - 允许报头VALUE =Content-Type的,授权接受/>
        <添加名称=访问控制允许的方法VALUE =GET,POST,PUT,DELETE,OPTIONS
    < / customHeaders>
< / httpProtocol>


下面显示的

是整个Excel方法。

  [HTTPGET]
    //获取API / myController的/ EXCEL
    公众的Htt presponseMessage Excel文件(字符串SIDX,串SORD,诠释页,诠释行,串得宝,串PDID,用户字符串,字符串属性,字符串值)
    {
        如果(AuthHelper.AuthService.HasValidCredentials(请求))
        {
            VAR gridResult = this.GetDataJqGridFormat(SIDX,SORD,页,行,得宝,PDID,用户,属性,数值);            //生成一个HTML表格。
            StringBuilder的建设者=新的StringBuilder();
            //我们创建一个HTML表:
            builder.Append(<表边框= 1>);
            builder.Append(< TR>< TD>站及LT; / TD>中);
            builder.Append(&所述; TD> PDID&下; / TD>中);
            builder.Append(&所述; TD>用户与所述; / TD>中);
            builder.Append(< TD>物业及LT; / TD>中);
            builder.Append(< TD> VALUE< / TD>< / TR>中);
            //创建匿名类型的响应
            的foreach(在gridResult.rows VAR项)
            {
                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(< /表>);            HTT presponseMessage结果=新HTT presponseMessage(的HTTPStatus code.OK);
            result.Content =新的StringContent(builder.ToString());
            result.Content.Headers.ContentType =新MediaTypeHeaderValue(应用程序/八位字节流);
            result.Content.Headers.ContentDisposition =新ContentDispositionHeaderValue(附件);
            result.Content.Headers.ContentDisposition.FileName =file.xls;            返回结果;
        }
        其他
        {
            扔ForbiddenResponseMessage();
        }    }

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

我要的是有文件下载的时候我打电话,它指向的Excel方法的URL。我不明白为什么它不会下载。它甚至有可能下载文件这样?


解决方案

好吧,我想通了。要饶你阅读,这是不可能做到这一点我想要的方式。

首先,我不能够使用jQuery Ajax来下载文件。正如我已经预期(或担心)是不可能下载使用Ajax的文件。这是出于安全考虑。
见<一href=\"http://stackoverflow.com/questions/14682556/why-threre-is-no-way-to-download-file-using-ajax-request\">Why threre是没有办法使用Ajax请求下载文件?

但我仍然需要下载该文件。借助上面的知识溶液/变通办法被似乎简单。

我改变了没有Ajax调用下面的JavaScript函数:

  VAR gridInfo = {
    SIDX:,
    SORD:ASC
    第二:1371046879480,
    行:50,
    页面:1
}
VAR有效载荷=? + prepareSearchQueryString()+&放大器; +连载(gridInfo);window.location的=HTTP://网站:59390 / API / myController的/创先争优+有效载荷

不过,因为我需要设置授权头这只涵盖了问题的一部分。

感谢谁回答我相关的问题(使用JavaScript 设置头)我的用户#1 SLaks能够找出网址输入用户名和密码,看起来像 https://开头的用户:password@domain.com/path查询可能是一个解决方案。可悲的是,它没有工作。它不是由IE和Chrome浏览器使用这种没有授权头被发现接受。

因为我也是用的jqGrid我能够设置使用的jqGrid授权头。使用本网站上的搜索功能时,这工作完全正常。我的JavaScript导出功能,现在看起来是这样的:

  VAR SURL =HTTP://本地主机:59390 / API / Wmssettings /创先争优?+ + prepareSearchQueryString();
$(#gridOverview)的jqGrid('为ExcelExport',{网址:SURL})。

但看我注意到,使用搜索功能时,不像有没有通过授权头的请求。我没有找出原因。当在jqGrid的源头找我注意到,网格只是做了window.location指向下载位置。而当这样做是没有办法传递基本的认证信息。

所以,对我来说,唯一的出路,就是我试图避免的方式。我改变了我的控制器方法返回包含一个URL指向该文件,然后我使用JavaScript做重定向到一个新的命名downloadcontroller控制器JSON。

控制器Excel的方法

  [HTTPGET]
    //获取API / wmssettings / EXCEL
    公众的Htt presponseMessage Excel文件(字符串SIDX,串SORD,诠释页,诠释行,串得宝,串PDID,用户字符串,字符串属性,字符串值)
    {
        如果(AuthHelper.AuthService.HasValidCredentials(请求))
        {
            VAR gridResult = this.GetDataJqGridFormat(SIDX,SORD,页,行,得宝,PDID,用户,属性,数值);            //生成一个HTML表格。
            StringBuilder的建设者=新的StringBuilder();
            //我们创建一个HTML表:
            builder.Append(&LT;表边框= 1&GT;);
            builder.Append(&LT; TR&GT;&LT; TD&GT;站及LT; / TD&gt;中);
            builder.Append(&所述; TD&GT; PDID&下; / TD&gt;中);
            builder.Append(&所述; TD&gt;用户与所述; / TD&gt;中);
            builder.Append(&LT; TD&GT;物业及LT; / TD&gt;中);
            builder.Append(&LT; TD&GT; VALUE&LT; / TD&GT;&LT; / TR&gt;中);
            //创建匿名类型的响应
            的foreach(在gridResult.rows VAR项)
            {
                builder.Append(&下; / TR&gt;中);
                builder.Append(&所述; TR&gt;中);
                builder.Append(&所述; TD&gt;中+ item.cell [0] +&下; / TD&gt;中);
                builder.Append(&所述; TD&gt;中+ item.cell [2] +&下; / TD&gt;中);
                builder.Append(&所述; TD&gt;中+ item.cell [3] +&下; / TD&gt;中);
                builder.Append(&所述; TD&gt;中+ item.cell [4] +&下; / TD&gt;中);
                builder.Append(&所述; TD&gt;中+ item.cell [5] +&下; / TD&gt;中);
            }
            builder.Append(&LT; /表&gt;);            //把所有的文件,并返回的网址:
            字符串文件名=出口+_+ Guid.NewGuid()的ToString()+的.xl​​s。
            使用(StreamWriter的作家=新的StreamWriter(HttpContext.Current.Server.MapPath(〜/下载+文件名)))
            {
                writer.Write(builder.ToString());
                writer.Flush();
            }
            HTT presponseMessage结果= Request.CreateResponse(的HTTPStatus code.OK,文件名);
            返回结果;
        }
        其他
        {
            扔ForbiddenResponseMessage();
        }
    }

JavaScript的导出方法

  VAR gridParams = {
    //ND:Math.floor((的Math.random()* 10000000)+ 1)
    SIDX:,
    SORD:ASC
    页:1,
    行:50
}有效载荷= prepareSearchQueryString()+&放大器; +连载(gridParams);VAR excelRequest = $阿贾克斯({
    网址:的http://本地主机:59390 / API / Wmssettings / Excel的,
    缓存:假的,
    输入:GET,
    数据:有效载荷,
    数据类型:JSON,
    的contentType:应用/ JSON的;字符集= UTF-8
});excelRequest.success(功能(数据,code,jqXHR){
    如果(数据== NULL){
        警报('SD');
        ShowErrorMessage(没有创建的文件。,,标题);
    }其他{
        window.location的=的http://本地主机:59390 / API /下载/文件名='+数据;
    }
});

我添加了以下线在我WebApiConfig.cs

  config.Routes.MapHttpRoute(Downloadcontroller,API / {控制器} / {行动},
             新{行动=GET},{新列举HTTPMethod =新HttpMethodConstraint(allowedVerbsGet),控制器=下载});

和最后的下载控制器:

 公共类DownloadController:ApiController
{
    [HTTPGET]
    公众的Htt presponseMessage获取(字符串文件名)
    {
        HTT presponseMessage结果=新HTT presponseMessage(的HTTPStatus code.OK);
        字符串fileLocation = HttpContext.Current.Server.MapPath(〜/下载+文件名);        如果(!File.Exists(fileLocation))
        {
            抛出新的Htt presponseException(的HTTPStatus code.OK);
        }        流FILESTREAM = File.Open(fileLocation,FileMode.Open);
        result.Content =新StreamContent(FILESTREAM);        result.Content.Headers.ContentType =新MediaTypeHeaderValue(应用程序/ vnd.ms - Excel中);
        result.Content.Headers.ContentDisposition =新ContentDispositionHeaderValue(附件);
        返回结果;
    }
}

以下几点可以得出结论:


  • 下载使用Ajax的文件是不可能的,而不是我要重定向到或precise文件位置或其他控制器。


  • 把URL中的用户名和密码在IE中是不允许的,似乎给在其他浏览器的问题。例如,在Chrome中的授权头保持为空。


  • 在做了window.location有没有办法通过附加头一样需要基本身份验证授权头。


嗯,就是这样。答案是我想做的事情是不可能在我preferred的方式。现在工作得很好。

I am using the ASP.NET Web API to build a prototype of a web service (and site) which has a method to download a file. When the user on the front-end presses the export button a jQuery ajax GET request is made and received by the controller which on it's turn calls the method named Excel (shown below). The method runs without any problem and finishes. When I look in Chrome at the header (see https://skydrive.live.com/redir?resid=2D85E5C937AC2BF9!77093) it receives the response with (as far as I am concerned) all the right headers.

I am using Basic Auth, so the user credentials are transmitted using the http authorization header which I have manually added to every jQuery Ajax request using Ajax Options.

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));
}

My prototype has some characteristics which I should mention:

  • Uses Basic Auth in Authorization header

  • The web service and the web site which calls the service are on different domains, so I used CORS and added the following to the 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>

Shown below is the entire Excel method.

    [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();
        }

    }

This is the header which should also return the file: https://skydrive.live.com/redir?resid=2D85E5C937AC2BF9!77093

What I want is to have the file downloaded when I call the url which points to the excel method. I do not understand why it won't download. Is it even possible to download a file this way?

解决方案

Alright I figured it out. To spare you the reading, it is not possible to do it the way I want.

First, I was not able to use jQuery Ajax to download the file. As I already expected (or feared) it is not possible to download a file using Ajax. This is due to security concerns. See Why threre is no way to download file using ajax request?

But I still need to download the file. With the above knowledge the solution/workaround was seemed simple.

I changed the javascript function which did the ajax call to the following:

    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

But this only covers part of the problem since I need to set the authorization header.

Thanks to Stackoverflow user SLaks who answered my related question (Set headers using javascript) I was able to find out that url with username and password looking like https://user:password@domain.com/path?query could be a solution. Sadly, it did not work. It wasn't accepted by IE and in Chrome using this there was no Authorization Header to be found.

Since I am also using jqGrid I was able to set the authorization head using jqGrid. This works perfectly fine when using the Search function on the site. My Javascript Export function now looks like this:

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

But looking at the request I noticed that unlike when using the Search function there are no authorization headers passed. I did find out the reason. When looking at the jqGrid source I noticed that the Grid is just doing a window.location to point to the download location. And when doing that there is no way to pass the basic auth information.

So the only way for me to go, is the way I was trying to avoid. I changed my controller method to return a json containing a url pointing to the file and then I use javascript to do the redirecting to a new controller named 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 Export Method

    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;
    }
});

I added the following line in my WebApiConfig.cs

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

And finally the download controller:

    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;
    }
}

The following things can be concluded:

  • Downloading a file using Ajax is not possible, instead I have to redirect to or the precise file location or the another controller.

  • Putting the username and password in the url is not allowed in IE and seems to give problems in other browsers. For example, in Chrome the authorization header stays empty.

  • When doing a window.location there is no way to pass additional headers like the authorization header needed for basic auth.

Well that's it. The answer is that the things I wanted to do are not possible in the way I preferred it. It works fine now.

这篇关于使用jQuery Ajax和基本身份验证的Web API将无法下载文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆