ASP.NET WebAPI2 CORS:预检时 GetOwinContext 中的空请求
Posted
技术标签:
【中文标题】ASP.NET WebAPI2 CORS:预检时 GetOwinContext 中的空请求【英文标题】:ASP.NET WebAPI2 CORS: null request in GetOwinContext on preflight 【发布时间】:2014-08-17 14:28:07 【问题描述】:我正在创建一个带有 WebAPI2 后端的 AngularJS (Typescript) SPA,需要来自 API 的身份验证和授权。 API 托管在不同的服务器上,因此我使用 CORS,主要遵循 http://www.codeproject.com/Articles/742532/Using-Web-API-Individual-User-Account-plus-CORS-En 上的指导,因为我是该领域的新手。
一切正常,我可以注册和登录,然后通过在客户端传递接收到的访问令牌,向受限访问控制器操作(这里是默认 VS WebAPI 2 模板中的虚拟“值”控制器)发出请求 -使用此相关代码的辅助服务:
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;
现在,我想在登录后检索用户的角色,以便 AngularJS 应用程序能够做出相应的行为。因此,我在我的帐户 API 中添加了此方法(在类级别具有属性 [Authorize]
、[RoutePrefix("api/Account")]
、[EnableCors(origins: "*", headers: "*", methods: "*")]
(*
用于测试目的):
[Route("UserRoles")]
public string[] GetUserRoles()
return UserManager.GetRoles(User.Identity.GetUserId()).ToArray();
然后我将此代码添加到我的登录控制器:
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 方法收到一个空请求。这是错误堆栈跟踪的开头:
"message":"An error has occurred.","exceptionMessage":"Value cannot be null.\r\nParameter name: request","exceptionType":"System.ArgumentNullException","stackTrace":" at System.Net.Http.OwinHttpRequestMessageExtensions.GetOwinContext(HttpRequestMessage request)\r\n at Accounts.Web.Controllers.AccountController.get_UserManager() ...
然而,我用于获取角色的代码与我用于从 WebAPI 测试控制器获取虚拟“值”的代码没有什么不同。我不能完全理解为什么需要预检的原因,但无论如何我都会在 OWIN 代码中遇到这个讨厌的异常。
我的请求标头是(API 位于端口 49592):
OPTIONS /api/account/userroles HTTP/1.1
Host: localhost:49592
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:64036
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/35.0.1916.153 Safari/537.36
Access-Control-Request-Headers: accept, authorization
Accept: */*
Referer: http://localhost:64036/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,it;q=-5.4
谁能解释一下?
【问题讨论】:
你确定 User.Identity.GetUserId() 有值吗? 我无法在此处设置断点,异常在我的代码之外和之前由管道中的 OWIN 内容引发。我也这么认为,但后来我看到断点永远不会被命中,并且堆栈跟踪指向参数“request”为空的那个 OWIN 方法。 我必须补充一点,我尝试了以下操作:(a) 按照***.com/questions/13624386/… 中的建议,我添加了帖子中指定的 Application_BeginRequest,但这会触发另一个异常:System.Web.HttpException: Server cannot在发送 HTTP 标头后设置状态; (b)按照文档中的建议,我用 [AcceptVerbs(new[] "GET")] 修饰了控制器操作,没有 OPTION 以避免 MVC 与 OPTIONS 请求混淆,但这似乎没有任何效果。还有其他想法吗? 您是否确保在 web.config 中已将允许的动词设置为包含选项? 谢谢,我的 web.config 基本上没有受到 webapi 标准模板的影响。我能找到的对 OPTIONS 的唯一引用是在 system.webServer/handlers 下:我想我找到了某种可行的解决方案,即使它看起来有些脏,但至少它有效。我将它发布在这里,以便其他人最终可以利用它,但我愿意接受建议。 (抱歉格式不好,但我尝试了几次,编辑器不允许我正确标记代码)。
基本上,解决方案是由这篇帖子的答案提出的:Handling CORS Preflight requests to ASP.NET MVC actions,但我更改了对我不起作用的代码(WebAPI 2 和 .NET 4.5.1)。就是这样:
在Global.asax
,方法Application_Start
,添加BeginRequest += Application_BeginRequest;
。
添加覆盖,它只是通过允许一切来响应OPTIONS
请求(这在我的测试环境中是可以的):
protected void Application_BeginRequest(object sender, EventArgs e) if ((Request.Headers.AllKeys.Contains("Origin")) && (Request.HttpMethod == "选项")) Response.StatusCode = 200; Response.Headers.Add("Access-Control-Allow-Origin", "*"); Response.Headers.Add("Access-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控制器方法的属性就是RouteAttribute
:
[路线(“用户角色”)] 公共字符串[] GetUserRoles() 字符串 id = User.Identity.GetUserId(); Debug.Assert(id != null); string[] aRoles = UserManager.GetRoles(id).ToArray(); 返回一个角色;
这样OPTIONS
请求会得到正确的响应,并且后续的 GET 会成功。
添加
我还必须补充一点,EnableCors 属性是不够的,因为我们不仅要处理 OPTIONS
动词,还要确保任何 CORS 请求都获得 Access-Control-Allow-Origin
标头。否则,您可能会观察到明显正确的响应(代码 200 等),但会看到 $http
调用失败。在我的情况下,我添加到 global.asax
这一行:
GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsAllowOriginHandler());
我的CorsAllowOriginHandler
是一个DelegatingHandler
,它只是确保这个值为*
的标头出现在请求包含Origin
标头的每个响应中:
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;
【讨论】:
以上是关于ASP.NET WebAPI2 CORS:预检时 GetOwinContext 中的空请求的主要内容,如果未能解决你的问题,请参考以下文章
ASP.NET Core:带有 OPTIONS 异常的 Windows 身份验证(CORS 预检)
来自 ASP NET Web API 的 CORS 阻止 Ajax POST 调用 - 对预检请求的响应未通过访问控制检查
使用 Windows 身份验证启用 CORS ASP.NET Web API 2 应用程序中的预检请求
React + ASP.Net Core 3:CORS 对预检请求的响应未通过访问控制检查:没有“Access-Control-Allow-Origin”标头