ASP.NET WebApi2启用CORS与AspNet.WebApi.Cors 5.2.3不兼容。

86
我尝试按照http://enable-cors.org/server_aspnet.html上的步骤操作,让我用ASP.NET WebAPI2实现的RESTful API能够处理跨域请求(启用CORS)。除非我修改web.config文件,否则它无法正常工作。
我安装了WebApi Cors依赖项:
install-package Microsoft.AspNet.WebApi.Cors -ProjectName MyProject.Web.Api

然后在我的App_Start文件夹中,我有一个名为WebApiConfig的类,代码如下:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var corsAttr = new EnableCorsAttribute("*", "*", "*");
        config.EnableCors(corsAttr);

        var constraintsResolver = new DefaultInlineConstraintResolver();

        constraintsResolver.ConstraintMap.Add("apiVersionConstraint", typeof(ApiVersionConstraint));
        config.MapHttpAttributeRoutes(constraintsResolver); 
        config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config));
        //config.EnableSystemDiagnosticsTracing(); 
        config.Services.Replace(typeof(ITraceWriter), new SimpleTraceWriter(WebContainerManager.Get<ILogManager>())); 
        config.Services.Add(typeof(IExceptionLogger), new SimpleExceptionLogger(WebContainerManager.Get<ILogManager>()));
        config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler()); 
    }
}
但在运行应用程序后,我使用Fiddler请求资源,如下: http://localhost:51589/api/v1/persons 但是在响应中,我不能看到应该看到的HTTP头,例如:
  • Access-Control-Allow-Methods: POST, PUT, DELETE, GET, OPTIONS
  • Access-Control-Allow-Origin: *
我是否漏掉了某些步骤?我尝试在控制器上使用以下注释: [EnableCors(origins: "http://example.com", headers: "*", methods: "*")] 相同的结果,CORS仍未启用。
然而,如果我在我的web.config文件中添加以下内容(甚至不需要安装AspNet.WebApi.Cors依赖项),它就可以工作:
<system.webServer>

<httpProtocol>
  <!-- THESE HEADERS ARE IMPORTANT TO WORK WITH CORS -->
  <!--
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
    <add name="Access-Control-Allow-Methods" value="POST, PUT, DELETE, GET, OPTIONS" />
    <add name="Access-Control-Allow-Headers" value="content-Type, accept, origin, X-Requested-With, Authorization, name" />
    <add name="Access-Control-Allow-Credentials" value="true" />
  </customHeaders>
  -->
</httpProtocol>
<handlers>
  <!-- THESE HANDLERS ARE IMPORTANT FOR WEB API TO WORK WITH  GET,HEAD,POST,PUT,DELETE and CORS-->
  <!--

  <remove name="WebDAV" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,PUT,DELETE" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
  <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>

非常感谢任何帮助!

谢谢。


1
我曾经遇到过同样的问题,但我决定在控制器中直接设置Access-Control-Allow-Origin头:HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);//如果需要,添加一些检查 response.Headers.Add("Access-Control-Allow-Origin", "*"); - Veselin Vasilev
11个回答

101

我为您创建了一个简化版的演示项目。

您可以尝试从本地 Fiddler 访问上述 API链接 以查看标头。这是一个解释。

Global.ascx

这只是调用WebApiConfig的代码组织形式,不做任何其他事情。

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        WebApiConfig.Register(GlobalConfiguration.Configuration);
    }
}

WebApiConfig.cs

这里的关键方法是 EnableCrossSiteRequests。 这就是你需要做的全部内容EnableCorsAttribute全局范围的CORS属性

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        EnableCrossSiteRequests(config);
        AddRoutes(config);
    }

    private static void AddRoutes(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "Default",
            routeTemplate: "api/{controller}/"
        );
    }

    private static void EnableCrossSiteRequests(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute(
            origins: "*", 
            headers: "*", 
            methods: "*");
        config.EnableCors(cors);
    }
}

值控制器

Get方法接收我们全局应用的EnableCors属性。 Another方法覆盖了全局的EnableCors

public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { 
            "This is a CORS response.", 
            "It works from any origin." 
        };
    }

    // GET api/values/another
    [HttpGet]
    [EnableCors(origins:"http://www.bigfont.ca", headers:"*", methods: "*")]
    public IEnumerable<string> Another()
    {
        return new string[] { 
            "This is a CORS response. ", 
            "It works only from two origins: ",
            "1. www.bigfont.ca ",
            "2. the same origin." 
        };
    }
}

Web.config

您无需在web.config中添加任何特殊内容。实际上,演示的web.config就是这样的 - 它是空的。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
</configuration>

演示

var url = "https://cors-webapi.azurewebsites.net/api/values"

$.get(url, function(data) {
  console.log("We expect this to succeed.");
  console.log(data);
});

var url = "https://cors-webapi.azurewebsites.net/api/values/another"

$.get(url, function(data) {
  console.log(data);
}).fail(function(xhr, status, text) {
  console.log("We expect this to fail.");
  console.log(status);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


22
我按照您的回答和此处链接中列出的步骤进行操作:http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api#enable-cors。尝试了多种组合,除了与问题中类似的`Web.config`-> <customHeaders>方法,没有任何启用CORS的结果。我正在使用最新的软件包(与问题中相同)。您有什么建议可以解决问题吗?对我而言,某种原因导致这种方法无法解决相同的问题。 - yǝsʞǝla
9
“Fixed it”意为“已修复”。出于某种原因,当从VisualStudio运行时,在本地主机上没有设置CORS标头,但在生产(在真实的IIS上)上会设置。我不知道为什么,我猜这可能是有条件的。我已经修复了这个问题。 - yǝsʞǝla
2
在我的本地IIS上,我有不同端口号的独立应用程序。Shaun的解决方案在没有Web.Config补丁的情况下无法工作,直到我注意到Shaun在WebApiConfig中使用了“*”作为来源。因此,我将“*”作为控制器中EnableCorsAttribute的来源放入,现在它可以在没有WebConfig的情况下工作。我对如何指定来源的阅读让我相信端口号无效,但我没有注意到“*”是可以的。此外,大多数示例都调用config.EnableCors(cors);而不带参数。例如:(http://enable-cors.org/server_aspnet.html) - Rob Von Nesselrode
14
浪费了3个小时试图让它工作后,我只是在Web配置文件中添加了3个头信息,实际上才使这个工作起来。参考链接:https://dev59.com/cmIk5IYBdhLWcg3wn_j1#21458845 - gyozo kudor
4
@ShaunLuttin 我想我错了,它实际上是有效的。似乎框架会检查来源,并且仅在来源在允许的来源列表中时发送标头。当来源不在列表中时,框架不发送标头(而我错误地将其解释为错误行为,因为我期望在任何响应的标头中看到允许的来源)。我将进行测试并报告结果。 - Aleksei Poliakov
显示剩余10条评论

16

你只需要更改一些文件。这对我有效。

Global.ascx

public class WebApiApplication : System.Web.HttpApplication {
    protected void Application_Start()
    {
        WebApiConfig.Register(GlobalConfiguration.Configuration);
    } }

WebApiConfig.cs

所有的请求都必须调用此代码。

public static class WebApiConfig {
    public static void Register(HttpConfiguration config)
    {
        EnableCrossSiteRequests(config);
        AddRoutes(config);
    }

    private static void AddRoutes(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "Default",
            routeTemplate: "api/{controller}/"
        );
    }

    private static void EnableCrossSiteRequests(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute(
            origins: "*", 
            headers: "*", 
            methods: "*");
        config.EnableCors(cors);
    } }

某个控制器

不需要更改。

Web.config

您需要在web.config中添加处理程序。

<configuration> 
  <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> 
</configuration>

1
最终我的问题是WebConfig。 - Geomorillo
1
配置更改对我起了作用。我已经: 删除处理程序 ExtensionlessUrlHandler-ISAPI-4.0_32bit ExtensionlessUrlHandler-ISAPI-4.0_64bit ExtensionlessUrlHandler-Integrated-4.0 WebDAV 并添加处理程序 ExtensionlessUrlHandler-ISAPI-4.0_32bit ExtensionlessUrlHandler-ISAPI-4.0_64bit ExtensionlessUrlHandler-Integrated-4.0..... 这导致它无法工作。根据这个答案的更改修复了它。谢谢 - biso

14

对于跨源资源共享请求,所有现代浏览器都会响应一个OPTIONS动词,然后才会进行实际的请求。这是用于在CORS请求时提示用户确认的。但是对于API而言,如果您想跳过此验证过程,请将以下片段添加到Global.asax中。

        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
            if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
            {
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, PUT, DELETE");

                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
                HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
                HttpContext.Current.Response.End();
            }
        }

这里我们只是通过检查OPTIONS动词来跳过检查。


1
谢谢。难道不是应该由依赖项Microsoft.AspNet.WebApi.Cors处理http级别的配置,这样我们就不需要在代码中显式地进行配置吗? 我可以通过在web.config中执行类似的操作来使其工作,但关键是要使用Microsoft.AspNet.WebApi.Cors来处理它,并且具有属性可配置性。 - diegosasw
不确定为什么您想完全跳过验证过程。至少在生产环境中不应该这样做。如果您想按设计使用内置的东西,可以正确配置CORS,然后忽略我的答案中详细说明的OPTIONS请求路由。 - BritishDeveloper
1
如果你发现IE可以工作,而Chrome和Firefox无法工作,那么这很可能是解决方案。在我的开发环境中,它起作用了。 - Bleeped
@neeraj 在尝试了几十种方法后,这是唯一能使其正常工作的方法。它仍然是可接受和安全的吗? - Connie DeCinko

8
我刚刚把自定义标头添加到Web.config中,效果很好。
在配置的system.webServer下:
<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
    <add name="Access-Control-Allow-Headers" value="Content-Type" />
  </customHeaders>
</httpProtocol>

我有前端应用和后端在同一个解决方案中。为了使其工作,我需要将Web服务项目(后端)设置为默认值。

我使用的是ReST,没有尝试其他方法。


正如其他人所说,浪费了时间试图让它“正常”工作,然后将其塞入了web.config文件。 - Morvael
1
谢谢@Mathter,虽然我启用了CORS通过我的启动类,但这对我有效。问题是,为什么我们不是按照常规方式来设置它,而是要在web.config中设置它? - Ciaran Gallagher

4
在我的Web.config进行了一些修改之后,我的Web API 2项目中的CORS突然停止工作了(至少在预检测期间的OPTIONS请求上)。看起来你需要在Web.config中包含下面提到的部分,否则(全局)EnableCorsAttribute将无法在OPTIONS请求上工作。请注意,这与Visual Studio在新的Web API 2项目中添加的完全相同的部分。
<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>

2

这些答案都不太可行。正如其他人所指出的,Cors包只有在请求中有Origin头时才会使用Access-Control-Allow-Origin头。但你通常不能仅仅添加一个Origin头到请求中,因为浏览器可能也会尝试对其进行调整。

如果你想要一种快速而简单的方法来允许跨站点请求到Web API,那么编写自定义过滤器属性真的更容易:

public class AllowCors : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (actionExecutedContext == null)
        {
            throw new ArgumentNullException("actionExecutedContext");
        }
        else
        {
            actionExecutedContext.Response.Headers.Remove("Access-Control-Allow-Origin");
            actionExecutedContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");
        }
        base.OnActionExecuted(actionExecutedContext);
    }
}

然后在您的控制器操作中使用它:

[AllowCors]
public IHttpActionResult Get()
{
    return Ok("value");
}

我不能保证这种方法的安全性,但相比在web.config中设置头信息,这种方法更加安全,因为你可以根据需要只应用到特定的地方。

当然,修改上述代码以允许特定来源、方法等也很简单。


不要认为这在Visual Studio调试情况下有效。看起来这段代码甚至没有被执行,因为Visual Studio的IIS在此代码之前就已经处理了请求。 - Ryan

2
我刚刚遇到了同样的问题,尝试全局启用CORS。然而,我发现它确实可以工作,但只有当请求包含Origin头值时才能工作。如果省略origin头值,响应将不包含Access-Control-Allow-Origin
我使用一个名为DHC的Chrome插件测试我的GET请求。它让我轻松地添加了Origin头。

1

对我来说,没有一个安全的解决方案适合我,所以为了比Neeraj更安全,比Matthew更容易,只需添加以下内容:

System.Web.HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");

在您的控制器方法中。这对我有效。

public IHttpActionResult Get()
{
    System.Web.HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
    return Ok("value");
}

我使用了CORS库并将属性放在控制器上,但它没有起作用。只有在WebApiConfig全局内使用它才有效。对于每个控制器来说,使用这种解决方案最终对我起作用了。谢谢。 - tno2007

0
希望这能帮助未来的某个人。我的问题是,我正在按照与OP相同的教程启用全局CORS。然而,在我的AccountController.cs文件中,我还设置了一个特定操作的CORS规则:
[EnableCors(origins: "", headers: "*", methods: "*")]

我遇到了一个关于“源不能为空或空字符串”的错误。但是这个错误发生在Global.asax.cs文件中,这真是个奇怪的地方。解决方法是将其更改为:

[EnableCors(origins: "*", headers: "*", methods: "*")]

注意到起源中的*了吗?缺少它是导致Global.asax.cs文件出错的原因。

希望这能帮助到某些人。


0
我发现这个问题是因为大多数浏览器发送的OPTIONS请求出现了问题。我的应用程序正在路由OPTIONS请求并使用我的IoC构造大量对象,其中一些由于各种原因在这种奇怪的请求类型上抛出异常。 如果OPTIONS请求给你带来麻烦,可以简单地忽略所有OPTIONS请求的路由:
var constraints = new { httpMethod = new HttpMethodConstraint(HttpMethod.Options) };
config.Routes.IgnoreRoute("OPTIONS", "{*pathInfo}", constraints);

更多信息:停止Web API处理OPTIONS请求


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接