Angularjs ASP.NET WebAPI2 CORS:飞行前GetOwinContext中的空请求
我正在创建一个带有WebAPI2后端的AngularJS Typescript SPA,需要API的身份验证和授权。API托管在不同的服务器上,所以我使用CORS,主要遵循在上的指导,因为我是这个领域的新手 一切正常,我可以注册和登录,然后通过在客户端服务中传递接收到的访问令牌,并使用以下相关代码,从默认VS WebAPI 2模板向受限访问控制器操作(此处为虚拟值控制器)发出请求:Angularjs ASP.NET WebAPI2 CORS:飞行前GetOwinContext中的空请求,angularjs,cors,owin,asp.net-web-api2,Angularjs,Cors,Owin,Asp.net Web Api2,我正在创建一个带有WebAPI2后端的AngularJS Typescript SPA,需要API的身份验证和授权。API托管在不同的服务器上,所以我使用CORS,主要遵循在上的指导,因为我是这个领域的新手 一切正常,我可以注册和登录,然后通过在客户端服务中传递接收到的访问令牌,并使用以下相关代码,从默认VS WebAPI 2模板向受限访问控制器操作(此处为虚拟值控制器)发出请求: private buildHeaders() { if (this.settings.token) {
private buildHeaders() {
if (this.settings.token) {
return { "Authorization": "Bearer " + this.settings.token };
}
return undefined;
}
public getValues(): ng.IPromise<string[]> {
var deferred = this.$q.defer();
this.$http({
url: this.config.rootUrl + "api/values",
method: "GET",
headers: this.buildHeaders(),
}).success((data: string[]) => {
deferred.resolve(data);
}).error((data: any, status: any) => {
deferred.reject(status.toString() + " " +
data.Message + ": " +
data.ExceptionMessage);
});
return deferred.promise;
}
然后,我将此代码添加到我的登录控制器:
private loadUserRoles() {
this.accountService.getUserRoles()
.then((data: string[]) => {
// store roles in an app-settings service
this.settings.roles = data;
}, (reason) => {
this.settings.roles = [];
});
}
public login() {
if ((!this.$scope.name) || (!this.$scope.password)) return;
this.accountService.loginUser(this.$scope.name,
this.$scope.password)
.then((data: ILoginResponseModel) => {
this.settings.token = data.access_token;
// LOAD ROLES HERE
this.loadUserRoles();
}, (reason) => {
this.settings.token = null;
this.settings.roles = [];
});
}
其中,账户控制员的方法是:
public getUserRoles() : ng.IPromise<string[]> {
var deferred = this.$q.defer();
this.$http({
url: this.config.rootUrl + "api/account/userroles",
method: "GET",
headers: this.buildHeaders()
}).success((data: string[]) => {
deferred.resolve(data);
}).error((data: any, status: any) => {
deferred.reject(status.toString() + ": " +
data.error + ": " +
data.error_description);
});
return deferred.promise;
}
无论如何,这会触发一个OPTIONS飞行前请求,从而导致500错误。如果我检查响应,我可以看到GetOwinContext方法得到一个空请求。以下是错误堆栈跟踪的开始:
{消息:发生错误,异常消息:值不能为空。\r\n参数名称:请求,异常类型:System.ArgumentNullException,stackTrace:at System.Net.Http.OwinHttpRequestMessageExtensions.GetowInTextHttpRequestMessage请求\r\n at Accounts.Web.Controllers.AccountController.get\u UserManager…}
然而,我用于获取角色的代码与我用于从WebAPI测试控制器获取虚拟值的代码没有什么不同。我不能确切地理解为什么这里需要预飞行,但无论如何,我在OWIN代码中遇到了这个令人讨厌的异常
我的请求头是端口49592处的API:
选项/api/account/userroles HTTP/1.1
主机:本地主机:49592
连接:保持活力
访问控制请求方法:GET
来源:http://localhost:64036
用户代理:Mozilla/5.0 Windows NT 6.3;WOW64 AppleWebKit/537.36 KHTML,如Gecko Chrome/35.0.1916.153 Safari/537.36
访问控制请求头:接受、授权
接受:*/*
推荐人:http://localhost:64036/
接受编码:gzip、deflate、sdch
接受语言:en-US,en;q=0.8,it;q=-5.4
有人能解释一下吗?我想我找到了某种有效的解决方案,即使它看起来有点脏,但至少它是有效的。我把它贴在这里,这样其他人最终可以利用它,但我愿意接受建议。很抱歉,格式错误,但我尝试了几次,编辑器不允许我正确标记代码 从本质上说,解决方案是由对这篇文章的回答提出的:,但我更改了不适用于我的WebAPI 2和.NET 4.5.1的代码。这是: 在Global.asax的方法Application\u Start中,添加BeginRequest+=Application\u BeginRequest 添加覆盖,它通过允许在我的测试环境中一切正常来响应选项请求: 受保护的无效应用程序\u开始请求对象发送方,事件参数e { if Request.Headers.AllKeys.ContainsOrigin&& Request.HttpMethod==选项 { Response.StatusCode=200; Response.Headers.AddAccess-Control-Allow-Origin,*; Response.Headers.AddAccess-Control-Allow-Methods、GET、PUT、POST、DELETE
string sRequestedHeaders = String.Join(", ",
Request.Headers.GetValues("Access-Control-Request-Headers") ?? new string[0]);
if (!String.IsNullOrEmpty(sRequestedHeaders))
Response.Headers.Add("Access-Control-Allow-Headers", sRequestedHeaders);
Response.End();
}
}
修饰accounts controller方法的属性只是RouteAttribute:
[RouteUserRoles]
公共字符串[]GetUserRoles
{
string id=User.Identity.GetUserId;
Debug.asserted!=null;
字符串[]aRoles=UserManager.GetRolesid.ToArray;
返回aRoles;
}
通过这种方式,OPTIONS请求得到正确的响应,随后的GET成功
加成
我还必须补充一点,EnableCors属性是不够的,因为我们不仅必须处理OPTIONS动词,还必须确保任何CORS请求都获得accesscontrolalloworigin头。否则,您可能会看到明显正确的响应代码200等,但看到$http调用失败。在我的例子中,我将这一行添加到global.asax:
GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsAllowOriginHandler());
My CorsAllowOriginHandler是一个DelegatingHandler,它只需确保在请求包含原始标头的每个响应中都存在此值为*的标头:
你确定User.Identity.GetUserId有一个值吗?我不能在那里设置断点,异常在我的代码之外和之前由管道中前面的OWIN抛出。我也这么认为,但后来我发现断点从未命中,堆栈跟踪指向参数请求为null的OWIN方法。我必须补充一点,我尝试了以下方法:a如中所建议,我添加了应用程序_BeginRequest,如文章中所指定,但这会触发另一个异常:System.Web.HttpException:发送HTTP头后,服务器无法设置状态;b正如文档中所建议的,我用[AcceptVerbsnew[]{GET}]修饰了控制器操作,但没有选项,以避免MVC对选项请求造成混乱,但这似乎没有任何效果。还有其他想法吗?您是否确保在web.config中设置了允许的动词包含选项?谢谢,我的web.config基本上没有受到webapi标准te的影响 安普拉特。我能找到的选项的唯一引用位于system.webServer/handlers:。我试着把它移走,但什么也没变。
GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsAllowOriginHandler());
public sealed class CorsAllowOriginHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync
(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
// all CORS-related headers must contain the Access-Control-Allow-Origin header,
// or the request will fail. The value may echo the Origin request header,
// or just be `*`.
if ((request.Headers.Any(h => h.Key == "Origin")) &&
(response.Headers.All(h => h.Key != "Access-Control-Allow-Origin")))
{
response.Headers.Add("Access-Control-Allow-Origin", "*");
}
return response;
}
}