3

下載文件我正在使用ASP.NET Web API構建一個具有下載文件方法的Web服務(和站點)的原型。當前端用戶按下導出按鈕時,由控制器創建並接收jQuery ajax GET請求,然後調用名爲Excel的方法(如下所示)。該方法運行沒有任何問題並完成。當我在標題中查看Chrome時(請參閱https://skydrive.live.com/redir?resid=2D85E5C937AC2BF9!77093),它會收到響應(就我而言)所有正確的標題。Web Api將不會使用jQuery Ajax和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服務和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。我不明白爲什麼它不會下載。這種方式甚至可以下載文件嗎?

回答

7

好吧我想通了。爲了讓你閱讀,不可能以我想要的方式進行閱讀。

首先,我無法使用jQuery Ajax下載文件。正如我已經預料(或擔心),不可能使用Ajax下載文件。這是由於安全問題。 請參閱Why threre is no way to download file using ajax request?

但我仍然需要下載該文件。有了上述知識,解決方案/解決方法似乎很簡單。

我改變了沒有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誰回答我的相關問題(Set headers using javascript)我能夠找出用戶名和密碼看起來像https://user:[email protected]/path?query URL可能是一個解決方案。可悲的是,它沒有奏效。它沒有被IE接受,並且在Chrome中使用它沒有找到授權標頭。

因爲我也使用jqGrid我能夠使用jqGrid設置授權頭。在網站上使用搜索功能時,此功能非常好。我的JavaScript導出功能現在看起來是這樣的:

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

但看我注意到,使用搜索功能時,不像有沒有通過授權頭的請求。我確實找出了原因。在查看jqGrid源代碼時,我注意到Grid只是在執行一個window.location來指向下載位置。而當這樣做時,沒有辦法傳遞基本的認證信息。

所以我走的唯一途徑就是我試圖避免的方式。我改變了我的控制器方法來返回一個包含指向該文件的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("<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中,似乎給其他瀏覽器的問題。例如,在Chrome中,授權標頭保持空白。

  • 在執行window.location時,無法傳遞額外的頭文件,例如基本身份驗證所需的授權頭文件。

就是這樣。答案是,我想要做的事情不可能以我喜歡的方式進行。現在它工作正常。