处理对 ASP.NET MVC 操作的 CORS 预检请求
Posted
技术标签:
【中文标题】处理对 ASP.NET MVC 操作的 CORS 预检请求【英文标题】:Handling CORS Preflight requests to ASP.NET MVC actions 【发布时间】:2012-11-17 10:39:36 【问题描述】:我正在尝试对 ASP.NET MVC 控制器操作执行跨域 POST 请求。此控制器操作接受并使用各种参数。问题是当预检请求发生时,控制器动作实际上尝试执行 & 因为 OPTIONS 请求没有传递任何数据,控制器动作抛出 500 HTTP 错误。如果我删除使用参数的代码,或者参数本身,整个请求链就成功完成了。
如何实现的示例:
控制器动作
public ActionResult GetData(string data)
return new JsonResult
Data = data.ToUpper(),
JsonRequestBehavior = JsonRequestBehavior.AllowGet
;
客户端代码
<script type="text/javascript">
$(function ()
$("#button-request").click(function ()
var ajaxConfig =
dataType: "json",
url: "http://localhost:8100/host/getdata",
contentType: 'application/json',
data: JSON.stringify( data: "A string of data" ),
type: "POST",
success: function (result)
alert(result);
,
error: function (jqXHR, textStatus, errorThrown)
alert('Error: Status: ' + textStatus + ', Message: ' + errorThrown);
;
$.ajax(ajaxConfig);
);
);
</script>
现在,无论何时发生预检请求,它都会返回一个 500 HTTP 代码,因为“data”参数为空,因为 OPTIONS 请求没有传递任何值。
服务器应用程序已在我的本地 IIS 的 8100 端口上设置,运行客户端代码的页面设置在 8200 端口上以模拟跨域调用。
我还为主机(在 8100 上)配置了以下标头:
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: POST, GET
Access-Control-Allow-Origin: http://localhost:8200
我发现的一种解决方法是检查执行操作的 HTTP 方法,如果是 OPTIONS 请求,则只返回空白内容,否则执行操作代码。像这样:
public ActionResult GetData(string data)
if (Request.HttpMethod == "OPTIONS")
return new ContentResult();
else
return new JsonResult
Data = data.ToUpper(),
JsonRequestBehavior = JsonRequestBehavior.AllowGet
;
但我觉得这种方法很笨拙。我曾考虑将这种逻辑添加到 Attribute
,但即使这样也意味着装饰将使用 CORS 调用的每个操作。
有没有更优雅的解决方案来让这个功能发挥作用?
【问题讨论】:
所以我找到了一个可行的解决方案。对于每个请求,我检查它是否是 CORS 请求以及请求是否带有 OPTIONS 动词,表明它是预检请求。如果是,我只是发回一个空响应(当然只包含在 IIS 中配置的标头),从而否定控制器操作执行。
然后,如果客户端确认允许根据预检返回的标头执行请求,则执行实际的 POST 并执行控制器操作。我的代码示例:
protected void Application_BeginRequest()
if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) &&
Request.HttpMethod == "OPTIONS")
Response.Flush();
如前所述,这对我有用,但如果有人知道更好的方法,或者我当前的实现中存在任何缺陷,我将不胜感激。
【讨论】:
Cors 确实很有趣。我遇到了tinyurl.com/nxz65ac 的问题,我在我的WebAPI 上实现了。文件上传很小时工作正常,我可以上传倍数就好,但是当我选择一个更大的文件说大约 50MB 并尝试上传时,我得到“没有'Access-Control-Allow-Origin'标头存在于请求的资源。”在铬。事件虽然如果我选择多个文件,较小的文件可以正常上传,但大文件会使浏览器停止。我会创建一个新问题,但我不知道如何标记像你这样解决了令人痛苦的 Cors 问题的用户。有人知道怎么做吗? 这不是 CORS 问题,但奇怪的是 Chrome 说它是。 WebAPI 中有两个文件大小限制区域,我必须同时设置:***.com/a/17324840 这进入 global.asax 并且是您可以挂钩的应用程序级事件之一。 这对我来说不太奏效,但调试和检查使我做出了这个小改动。添加特定的标头并确保明确 命名 Access-Control-Allow-Headers(星号不够用)。if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS") Response.Headers.Add("Access-Control-Allow-Origin", "*"); Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, Session"); Response.Flush();
标头键不区分大小写,我遇到过 Chrome 中的 jQuery 会为 HTTP 发送“Origin”而为 HTTPS 发送“origin”的情况。不过,jQuery 和 Chrome 的这种组合不太可能做到这一点。我已更新您的答案以不区分大小写检查以解决此差异。【参考方案2】:
接受的答案就像一个魅力,但我发现请求实际上正在传递给控制器。我收到了200
状态代码,但响应正文包含大量 html,但控制器有异常。因此,与其使用Response.Flush()
,不如使用Response.End()
,它确实会停止请求的执行。这种替代解决方案如下所示:
编辑:修正了原始答案中的一个错字。
protected void Application_BeginRequest()
if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) &&
Request.HttpMethod == "OPTIONS")
Response.End();
【讨论】:
你把这个函数放在哪里了? 看起来它在应用程序根文件夹的 Global.asax.cs 中。【参考方案3】:以下是我使用 ASP.Net Web Api 处理预检/CORS 问题的方法。我只是将 Microsoft.AspNet.WebApi.Cors Nuget 包添加到我的 Web 项目中。然后在我的 WebApiConfig.cs 文件中我添加了这一行:
config.EnableCors(new ApplicationCorsPolicy());
并创建了一个自定义 PolicyProvider 类
public class ApplicationCorsPolicy : Attribute, ICorsPolicyProvider
public async Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var corsRequestContext = request.GetCorsRequestContext();
var originRequested = corsRequestContext.Origin;
if (await IsOriginFromAPaidCustomer(originRequested))
// Grant CORS request
var policy = new CorsPolicy
AllowAnyHeader = true,
AllowAnyMethod = true
;
policy.Origins.Add(originRequested);
return policy;
// Reject CORS request
return null;
private async Task<bool> IsOriginFromAPaidCustomer(string originRequested)
// Do database look up here to determine if origin should be allowed.
// In my application I have a table that has a list of domains that are
// allowed to make API requests to my service. This is validated here.
return true;
看,Cors 框架允许您添加自己的逻辑来确定允许哪些来源等。如果您将 REST API 公开给外部世界以及可以访问的人员(来源)列表,这将非常有用您的站点处于数据库等受控环境中。现在,如果您只是允许所有来源(在所有情况下这可能不是一个好主意),您可以在 WebApiConfig.cs 中执行此操作以全局启用 CORS:
config.EnableCors();
就像 WebApi 中的过滤器和处理程序一样,您也可以向控制器添加类或方法级别的注释,如下所示:
[EnableCors("*, *, *, *")]
请注意,EnableCors 属性有一个接受以下参数的构造函数
-
允许的来源列表
允许的请求标头列表
允许的 HTTP 方法列表
允许的响应标头列表
您可以在每个控制器/端点静态指定允许访问什么资源的人员。
2016 年 6 月 24 日更新: 我应该提到我的 Web.config 中有以下内容。看起来这些可能不是每个人的默认设置。
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
来源:Microsoft
【讨论】:
【参考方案4】:这些答案都不适合我,但以下 webconfig 设置可以。我的两个关键设置是将Access-Control-Allow-Headers
设置为Content-Type
并注释掉删除OPTIONSVerbHandler
的行:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"></modules>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
</customHeaders>
</httpProtocol>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<!--<remove name="OPTIONSVerbHandler" />-->
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
【讨论】:
需要注意的是,上面的代码对每个来源都启用了 CORS。通常,您想尽可能地尝试限制来源。您是否在 WebApiConfig.cs 文件中尝试过config.EnableCors()
?【参考方案5】:
扩展 Carl 的答案,我将他的代码插入到我的 OWIN 管道中:
app.Use((context, next) =>
if (context.Request.Headers.Any(k => k.Key.Contains("Origin")) && context.Request.Method == "OPTIONS")
context.Response.StatusCode = 200;
return context.Response.WriteAsync("handled");
return next.Invoke();
);
只需将其添加到 Startup.cs 中 IAppBuilder 的开头(或注册 WebAPI 之前的任何位置)
【讨论】:
【参考方案6】:这可能是一个红鲱鱼。我最近让 CORS 工作正常,没有跳过你正在做的任何麻烦。
这是使用 Thinktecture.IdentityModel nuget 包的组合完成的,更重要的是...删除所有对 WebDAV 的引用。这包括从 IIS 中删除 webdav 模块,并确保您的 web 配置中有以下几行:
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule" />
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
</modules>
<handlers>
<remove name="WebDAV" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
然后您可以使用 thinktecture 从 Global.asax 使用如下静态类配置 CORS:
public class CorsConfig
public static void RegisterCors(HttpConfiguration httpConfiguration)
var corsConfig = new WebApiCorsConfiguration();
corsConfig.RegisterGlobal(httpConfiguration);
corsConfig.ForAllResources().AllowAllOriginsAllMethodsAndAllRequestHeaders();
来源:http://brockallen.com/2012/06/28/cors-support-in-webapi-mvc-and-iis-with-thinktecture-identitymodel/
【讨论】:
我在研究期间一直在研究 Thinktecture,但这似乎是一件很简单的事情,我想找到一种无需第三方库的方式来实现它。不过,感谢您花时间发布您的答案:) 没问题,有时候自己做会更有益也更有趣:) 谢谢!您的回答为我指明了正确的方向。 CORS 为我工作,但我不小心从我的 web.config 中删除了 ExtensionlessUrlHandler 东西,这破坏了我的 CORS(我应该删除无异常的东西......)以上是关于处理对 ASP.NET MVC 操作的 CORS 预检请求的主要内容,如果未能解决你的问题,请参考以下文章
ASP.NET 5 MVC 6 CORS vue.js 请求不允许