Jer*_*oen 3 c# asp.net basic-authentication asp.net-mvc-4 asp.net-web-api
我正在使用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));
}
Run Code Online (Sandbox Code Playgroud)
我的原型有一些我应该提到的特征:
在Authorization标头中使用Basic Auth
Web服务和调用该服务的网站位于不同的域中,因此我使用CORS并将以下内容添加到web.config中
Run Code Online (Sandbox Code Playgroud)<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();
}
}
Run Code Online (Sandbox Code Playgroud)
这是标题,也应该返回文件: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
Run Code Online (Sandbox Code Playgroud)
但这只涉及部分问题,因为我需要设置授权标头.
感谢Stackoverflow用户SLaks回答了我的相关问题(使用javascript设置标头)我能够找到用户名和密码看起来像https:// user:password@domain.com/path?query的网址可能是一个解决方案.可悲的是,它没有用.它不被IE接受,在Chrome中使用它没有找到授权标题.
由于我也在使用jqGrid,我可以使用jqGrid设置授权头.在网站上使用搜索功能时,此功能完全正常.我的Javascript导出功能现在看起来像这样:
var sUrl = "http://localhost:59390/api/Wmssettings/excel" + "?" + PrepareSearchQueryString() ;
$("#gridOverview").jqGrid('excelExport', { url: sUrl });
Run Code Online (Sandbox Code Playgroud)
但是看看请求我注意到,与使用搜索功能时不同,没有传递授权标头.我确实找到了原因.在查看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();
}
}
Run Code Online (Sandbox Code Playgroud)
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;
}
});
Run Code Online (Sandbox Code Playgroud)
我在WebApiConfig.cs中添加了以下行
config.Routes.MapHttpRoute("Downloadcontroller", "api/{controller}/{action}",
new { action = "Get"}, new { httpMethod = new HttpMethodConstraint(allowedVerbsGet), controller="download"});
Run Code Online (Sandbox Code Playgroud)
最后下载控制器:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
可以得出以下结论:
使用Ajax下载文件是不可能的,而是我必须重定向到或者精确的文件位置或另一个控制器.
在IE中不允许将用户名和密码放在网址中,并且似乎在其他浏览器中出现问题.例如,在Chrome中,授权标头保持为空.
在执行window.location时,无法传递其他标头,例如基本身份验证所需的授权标头.
那就是它.答案是,我想要做的事情不可能以我喜欢的方式进行.它现在工作正常.