222

I have a simple actionmethod, that returns some json. It runs on ajax.example.com. I need to access this from another site someothersite.com.

If I try to call it, I get the expected...:

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

I know of two ways to get around this: JSONP and creating a custom HttpHandler to set the header.

Is there no simpler way?

Is it not possible for a simple action to either define a list of allowed origins - or simple allow everyone? Maybe an action filter?

Optimal would be...:

return json(mydata, JsonBehaviour.IDontCareWhoAccessesMe);
sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Kjensen
  • 12,447
  • 36
  • 109
  • 171

15 Answers15

409

For plain ASP.NET MVC Controllers

Create a new attribute

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

Tag your action:

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

For 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);
    }
}

Tag a whole API controller:

[AllowCrossSiteJson]
public class ValuesController : ApiController
{

Or individual API calls:

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

For Internet Explorer <= v9

IE <= 9 doesn't support CORS. I've written a javascript that will automatically route those requests through a proxy. It's all 100% transparent (you just have to include my proxy and the script).

Download it using nuget corsproxy and follow the included instructions.

Blog post | Source code

jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • 3
    in awe of the elegance of this solution – BraveNewMath Dec 14 '12 at 09:59
  • 3
    You could easily extend the attribute to accept a specific origin if you wanted to limit CORS to your own domains. – Petrus Theron Feb 22 '13 at 11:34
  • 2
    You should be able to add this to the RegisterHttpFilters in your App_Start\FilterConfig correct? Doing so would apply it to all of the Api controllers in your project. Coupling this with pate's comment above you could limit CORS to your domain(s) for all controllers. – bdwakefield Mar 12 '13 at 18:04
  • It also works if you have a controller base class and do it in the OnActionExecuting method. – Scott R. Frost May 28 '13 at 20:08
  • 1
    I found this method failed with the PUT http verb. Instead I used: http://brockallen.com/2012/06/28/cors-support-in-webapi-mvc-and-iis-with-thinktecture-identitymodel/ – GreyCloud Sep 03 '13 at 12:48
  • Microsoft now has a NuGet package that does this (and more) http://www.nuget.org/packages/Microsoft.AspNet.WebApi.Cors – jaypeagi May 29 '15 at 14:48
  • 13
    I've recently updated our project to MVC 5 and attempted to do this. Even adding the header in a filter doesn't seem to work. When I view the request in network the header is not present on the response. Is there anything else that needs done to get this to work? – Kneemin Mar 02 '16 at 20:31
  • I want to add a custom header during runtime, how do I pass the whitelist array of Url that can access the API? – Si8 Mar 30 '17 at 18:43
  • Unless I'm missing something, this attribute isn't JSON-exclusive – Jared Beach Apr 13 '20 at 17:10
127

If you are using IIS 7+, you can place a web.config file into the root of the folder with this in the system.webServer section:

<httpProtocol>
   <customHeaders>
      <clear />
      <add name="Access-Control-Allow-Origin" value="*" />
   </customHeaders>
</httpProtocol>

See: http://msdn.microsoft.com/en-us/library/ms178685.aspx And: http://enable-cors.org/#how-iis7

LaundroMatt
  • 2,158
  • 1
  • 19
  • 20
  • 1
    I can't remember why anymore, but this method does not always work in IIS 7+ – LaundroMatt Jan 09 '13 at 15:31
  • Hmm. The only reason I can think it wouldn't work is if a request originates from a non-CORS browser. But I'll continue to investigate. – sellmeadog Jan 09 '13 at 16:53
  • 34
    Also, this would make the entire website CORS-friendly. If somebody wants to mark only a single action or controller as CORS-friendly, then the accepted answer is much better. – Lev Dubinets Feb 08 '13 at 16:43
  • 1
    If you see the [ASP.Net](http://enable-cors.org/server_aspnet.html) section, it has a **hint**: *"Note: this approach is compatible with IIS6, IIS7 Classic Mode, and IIS7 Integrated Mode."* – percebus Dec 13 '13 at 20:28
  • I think the only draw back of this method is that it requires an additional step when deploying – JDandChips Feb 02 '14 at 12:32
  • @LaundroMatt I don't understand about the `` in this example. is this required for some reason? – Segfault Apr 14 '14 at 21:41
  • 1
    I am facing a cross- domain issue when I publish my app on the SharePoint environment. When I run my app on local environment my app runs fine, but when I publish it on azure to my sharepoint site it redirects to error page on Ajax.Begin form call. I tried this solution but it doesn't work for me. Is there any other alternative to it? – Jyotsna Wadhwani Sep 04 '17 at 07:29
26

I ran into a problem where the browser refused to serve up content that it had retrieved when the request passed in cookies (e.g., the xhr had its withCredentials=true), and the site had Access-Control-Allow-Origin set to *. (The error in Chrome was, "Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.")

Building on the answer from @jgauffin, I created this, which is basically a way of working around that particular browser security check, so caveat emptor.

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);
    }
}
Ken Smith
  • 20,305
  • 15
  • 100
  • 147
  • I tried this method but gives me error: `Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed` – Rak Jun 27 '22 at 02:00
21

This is really simple , just add this in 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>

In Origin put all domains that have access to your web server, in headers put all possible headers that any ajax http request can use, in methods put all methods that you allow on your server

regards :)

Zvonimir Tokic
  • 443
  • 9
  • 19
10

Sometimes OPTIONS verb as well causes problems

Simply: Update your web.config with the following

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

And update the webservice/controller headers with httpGet and httpOptions

// GET api/Master/Sync/?version=12121
        [HttpGet][HttpOptions]
        public dynamic Sync(string version) 
        {
soniiic
  • 2,664
  • 2
  • 26
  • 40
Bishoy Hanna
  • 4,539
  • 1
  • 29
  • 31
9

WebAPI 2 now has a package for CORS which can be installed using : Install-Package Microsoft.AspNet.WebApi.Cors -pre -project WebServic

Once this is installed, follow this for the code :http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

Tarun
  • 2,808
  • 3
  • 22
  • 21
5

Add this line to your method, If you are using a API.

HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*"); 
Gopichandar
  • 2,742
  • 2
  • 24
  • 54
5

This tutorial is very useful. To give a quick summary:

  1. Use the CORS package available on Nuget: Install-Package Microsoft.AspNet.WebApi.Cors

  2. In your WebApiConfig.cs file, add config.EnableCors() to the Register() method.

  3. Add an attribute to the controllers you need to handle cors:

[EnableCors(origins: "<origin address in here>", headers: "*", methods: "*")]

GrandMasterFlush
  • 6,269
  • 19
  • 81
  • 104
  • I had to use this method because I needed to set a custom header in my request, and the custom attribute method did not work with the browsers pre-flight request. This seems to work in all cases. – lehn0058 Feb 29 '16 at 19:25
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);
    }
Pranav Labhe
  • 1,943
  • 1
  • 19
  • 24
4

After struggling for a whole evening I finally got this to work. After some debugging I found the problem I was walking into was that my client was sending a so called preflight Options request to check if the application was allowed to send a post request with the origin, methods and headers provided. I didn't want to use Owin or an APIController, so I started digging and came up with the following solution with just an ActionFilterAttribute. Especially the "Access-Control-Allow-Headers" part is very important, as the headers mentioned there do have to match the headers your request will send.

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);
            }
        }
    }
}

Finally, my MVC action method looks like this. Important here is to also mention the Options HttpVerbs, because otherwise the preflight request will fail.

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

There are different ways we can pass the Access-Control-Expose-Headers.

  • As jgauffin has explained we can create a new attribute.
  • As LaundroMatt has explained we can add in the web.config file.
  • Another way is we can add code as below in the webApiconfig.cs file.

    config.EnableCors(new EnableCorsAttribute("", headers: "", methods: "*",exposedHeaders: "TestHeaderToExpose") { SupportsCredentials = true });

Or we can add below code in the Global.Asax file.

protected void Application_BeginRequest()
        {
            if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
            {
                //These headers are handling the "pre-flight" OPTIONS call sent by the browser
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "*");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "http://localhost:4200");
                HttpContext.Current.Response.AddHeader("Access-Control-Expose-Headers", "TestHeaderToExpose");
                HttpContext.Current.Response.End();
            }
        }

I have written it for the options. Please modify the same as per your need.

Happy Coding !!

Trilok Pathak
  • 2,931
  • 4
  • 28
  • 34
1

I'm using DotNet Core MVC and after fighting for a few hours with nuget packages, Startup.cs, attributes, and this place, I simply added this to the MVC action:

Response.Headers.Add("Access-Control-Allow-Origin", "*");

I realise this is pretty clunky, but it's all I needed, and nothing else wanted to add those headers. I hope this helps someone else!

Ben Power
  • 1,786
  • 5
  • 27
  • 35
0

In Web.config enter the following

<system.webServer>
<httpProtocol>
  <customHeaders>
    <clear />     
    <add name="Access-Control-Allow-Credentials" value="true" />
    <add name="Access-Control-Allow-Origin" value="http://localhost:123456(etc)" />
  </customHeaders>
</httpProtocol>
Elvis Skensberg
  • 151
  • 1
  • 9
0

If you use IIS, I'd suggest trying IIS CORS module.
It's easy to configure and works for all types of controllers.

Here is an example of configuration:

    <system.webServer>
        <cors enabled="true" failUnlistedOrigins="true">
            <add origin="*" />
            <add origin="https://*.microsoft.com"
                 allowCredentials="true"
                 maxAge="120"> 
                <allowHeaders allowAllRequestedHeaders="true">
                    <add header="header1" />
                    <add header="header2" />
                </allowHeaders>
                <allowMethods>
                     <add method="DELETE" />
                </allowMethods>
                <exposeHeaders>
                    <add header="header1" />
                    <add header="header2" />
                </exposeHeaders>
            </add>
            <add origin="http://*" allowed="false" />
        </cors>
    </system.webServer>
olsh
  • 464
  • 1
  • 6
  • 14
0

To make the main answer work in MVC 5.

Use System.Web.Mvc instead of System.Web.Http.Filters.

using System;
using System.Web.Mvc;
// using System.Web.Http.Filters;

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
   ...
}