在ASP.Net MVC中设置Access-Control-Allow-Origin——最简单的方法

222

我有一个简单的Action方法,返回一些JSON。它在ajax.example.com上运行。我需要从另一个站点someothersite.com访问它。

如果我尝试调用它,我会得到预期的结果...

Origin http://someothersite.com is not allowed by Access-Control-Allow-Origin.

我知道两种解决此问题的方法:JSONP和创建一个自定义HttpHandler来设置标头。

难道没有更简单的方式吗?

是否不可能让一个简单的操作来定义允许的来源列表 - 或者简单地允许每个人?也许是一个操作过滤器?

最好的情况是...:

return json(mydata, JsonBehaviour.IDontCareWhoAccessesMe);
15个回答

409

针对纯 ASP.NET MVC 控制器

创建一个新属性

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.RequestContext.HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*");
        base.OnActionExecuting(filterContext);
    }
}

为您的操作打标签:

[AllowCrossSiteJson]
public ActionResult YourMethod()
{
    return Json("Works better?");
}

对于ASP.NET Web API

using System;
using System.Web.Http.Filters;

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (actionExecutedContext.Response != null)
            actionExecutedContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");

        base.OnActionExecuted(actionExecutedContext);
    }
}

给整个API控制器标记:

[AllowCrossSiteJson]
public class ValuesController : ApiController
{

或单独的API调用:

[AllowCrossSiteJson]
public IEnumerable<PartViewModel> Get()
{
    ...
}

针对 Internet Explorer <= v9

IE <= 9 不支持CORS(跨域资源共享)。我编写了一段JavaScript代码,可以自动将这些请求通过代理路由。这是完全透明的(您只需包含我的代理和脚本即可)。

使用 nuget 下载它:corsproxy 并按照所附的说明进行操作。

博客文章 | 源代码


3
对这个解决方案的优雅之处感到惊叹。 - BraveNewMath
3
如果你想将CORS限制在自己的域名范围内,你可以轻松地扩展该属性以接受特定的来源。 - Petrus Theron
2
你应该能够将它添加到 App_Start\FilterConfig 中的 RegisterHttpFilters,对吗?这样做会将其应用于项目中的所有 Api 控制器。结合上面 pate 的评论,你可以限制所有控制器的 CORS 访问仅限于你的域名。 - bdwakefield
1
我发现这种方法在使用PUT http动词时失败了。相反,我使用了:http://brockallen.com/2012/06/28/cors-support-in-webapi-mvc-and-iis-with-thinktecture-identitymodel/ - GreyCloud
13
最近我将我们的项目更新到了MVC5并尝试做这件事。即使在过滤器中添加头信息似乎也无效。当我在网络中查看请求时,响应中没有该头信息。是否还需要进行其他操作才能让它起作用? - Kneemin
显示剩余4条评论

127

1
我已经记不清为什么了,但这种方法在 IIS 7+ 中并不总是有效。 - LaundroMatt
34
同时,这将使整个网站都支持CORS。如果有人只想标记单个动作或控制器为CORS友好,则接受的答案更好。 - Lev Dubinets
1
如果您查看ASP.Net部分,它有一个提示:“注意:此方法与IIS6、IIS7经典模式和IIS7集成模式兼容。” - percebus
我认为这种方法唯一的缺点是在部署时需要额外的步骤。 - JDandChips
1
当我在SharePoint环境中发布我的应用程序时,我遇到了跨域问题。当我在本地环境上运行我的应用程序时,一切都很正常,但是当我将其发布到Azure上的SharePoint站点时,在Ajax.Begin表单调用上会重定向到错误页面。我尝试了这个解决方案,但对我没有用。还有其他替代方法吗? - Jyotsna Wadhwani
显示剩余2条评论

26
我遇到了一个问题,当请求传递 cookies(例如 xhr 具有 withCredentials=true)并且站点设置了 Access-Control-Allow-Origin* 时,浏览器拒绝提供其检索到的内容。(在 Chrome 中的错误是“Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.”)
基于 @jgauffin 的答案,我创建了这个解决方案,它基本上是一种解决特定浏览器安全检查的方法,所以请注意。
public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // We'd normally just use "*" for the allow-origin header, 
        // but Chrome (and perhaps others) won't allow you to use authentication if
        // the header is set to "*".
        // TODO: Check elsewhere to see if the origin is actually on the list of trusted domains.
        var ctx = filterContext.RequestContext.HttpContext;
        var origin = ctx.Request.Headers["Origin"];
        var allowOrigin = !string.IsNullOrWhiteSpace(origin) ? origin : "*";
        ctx.Response.AddHeader("Access-Control-Allow-Origin", allowOrigin);
        ctx.Response.AddHeader("Access-Control-Allow-Headers", "*");
        ctx.Response.AddHeader("Access-Control-Allow-Credentials", "true");
        base.OnActionExecuting(filterContext);
    }
}

我尝试了这种方法,但是出现了错误:响应预检请求未通过访问控制检查:'Access-Control-Allow-Origin'标头包含多个值'*,*',但只允许一个 - Rak

21

这很简单,只需在web.config中添加此内容

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="http://localhost" />
      <add name="Access-Control-Allow-Headers" value="X-AspNet-Version,X-Powered-By,Date,Server,Accept,Accept-Encoding,Accept-Language,Cache-Control,Connection,Content-Length,Content-Type,Host,Origin,Pragma,Referer,User-Agent" />
      <add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, OPTIONS" />
      <add name="Access-Control-Max-Age" value="1000" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

在Origin中放置所有可以访问您的Web服务器的域, 在headers中放置任何Ajax HTTP请求可能使用的所有可能头部, 在methods中放置您允许在您的服务器上使用的所有方法。

祝好 :)


如果您打算使用已授权的查询,将“Authorization”添加到Access-Control-Allow-Headers中也会非常有用。 - AFract
很棒的答案!只需提醒包括本地主机端口号,以使其在IIS Express下工作。 - Matias Masso

10

有时OPTIONS动词也会导致问题。

简单来说,更新您的web.config文件如下:

<system.webServer>
    <httpProtocol>
        <customHeaders>
          <add name="Access-Control-Allow-Origin" value="*" />
          <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

并更新 Web 服务/控制器的头部为 httpGet 和 httpOptions

// GET api/Master/Sync/?version=12121
        [HttpGet][HttpOptions]
        public dynamic Sync(string version) 
        {

顺便提一下,在Sitefinity中,您需要在安全部分的系统高级设置中添加*。 - Bishoy Hanna
我需要更新哪些文件中的控制器头文件? - user3281466

9

5

这个教程非常有用。简单概括如下:

  1. 使用Nuget上可获得的CORS包:Install-Package Microsoft.AspNet.WebApi.Cors

  2. 在你的WebApiConfig.cs文件中,将config.EnableCors()添加到Register()方法中。

  3. 为需要处理cors的控制器添加一个属性:

[EnableCors(origins: "<此处输入来源地址>", headers: "*", methods: "*")]


我不得不使用这种方法,因为我需要在请求中设置自定义头部,而自定义属性方法在浏览器的预检请求中无法工作。这种方法似乎在所有情况下都有效。 - lehn0058

5

如果你正在使用API,请在你的方法中添加这一行代码。

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

4
    public ActionResult ActionName(string ReqParam1, string ReqParam2, string ReqParam3, string ReqParam4)
    {
        this.ControllerContext.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin","*");
         /*
                --Your code goes here --
         */
        return Json(new { ReturnData= "Data to be returned", Success=true }, JsonRequestBehavior.AllowGet);
    }

4

在经过整整一个晚上的努力后,我终于让它正常工作了。在一些调试后,我发现我遇到的问题是我的客户端正在发送所谓的预检测选项请求,以检查应用程序是否被允许使用提供的源、方法和标头发送帖子请求。我不想使用 Owin 或 APIController,因此我开始深入研究,并提出了以下解决方案,只使用了一个 ActionFilterAttribute。特别是“Access-Control-Allow-Headers”部分非常重要,因为那里提到的标头必须与您的请求将要发送的标头匹配。

using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MyNamespace
{
    public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            HttpRequest request = HttpContext.Current.Request;
            HttpResponse response = HttpContext.Current.Response;

            // check for preflight request
            if (request.Headers.AllKeys.Contains("Origin") && request.HttpMethod == "OPTIONS")
            {
                response.AppendHeader("Access-Control-Allow-Origin", "*");
                response.AppendHeader("Access-Control-Allow-Credentials", "true");
                response.AppendHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
                response.AppendHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-RequestDigest, Cache-Control, Content-Type, Accept, Access-Control-Allow-Origin, Session, odata-version");
                response.End();
            }
            else
            {
                HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
                HttpContext.Current.Response.Cache.SetNoStore();

                response.AppendHeader("Access-Control-Allow-Origin", "*");
                response.AppendHeader("Access-Control-Allow-Credentials", "true");
                if (request.HttpMethod == "POST")
                {
                    response.AppendHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
                    response.AppendHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-RequestDigest, Cache-Control, Content-Type, Accept, Access-Control-Allow-Origin, Session, odata-version");
                }

                base.OnActionExecuting(filterContext);
            }
        }
    }
}

最后,我的MVC操作方法看起来像这样。这里需要特别提到Options HttpVerbs,否则预检请求将失败。

[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Options)]
[AllowCrossSiteJson]
public async Task<ActionResult> Create(MyModel model)
{
    return Json(await DoSomething(model));
}

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