152

I tried everything that is written in this article: http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api, but nothing works. I'm trying to get data from webAPI2 (MVC5) to use in another domain using angularJS.

my controller looks like this:

namespace tapuzWebAPI.Controllers
{
    [EnableCors(origins: "http://local.tapuz.co.il", headers: "*", methods: "*", SupportsCredentials = true)]
    [RoutePrefix("api/homepage")]
    public class HomePageController : ApiController
    {
        [HttpGet]
        [Route("GetMainItems")]
        //[ResponseType(typeof(Product))]
        public List<usp_MobileSelectTopSecondaryItemsByCategoryResult> GetMainItems()
        {


            HomePageDALcs dal = new HomePageDALcs();
            //Three product added to display the data

            //HomePagePromotedItems.Value.Add(new HomePagePromotedItem.Value.FirstOrDefault((p) => p.ID == id));


            List<usp_MobileSelectTopSecondaryItemsByCategoryResult> items = dal.MobileSelectTopSecondaryItemsByCategory(3, 5);
            return items;

        }      
    }
}
Mikhail
  • 9,186
  • 4
  • 33
  • 49
Noa Gani
  • 1,521
  • 2
  • 10
  • 3
  • 1
    Also share your angular code for requesting cors – harishr Dec 16 '14 at 12:07
  • 3
    There is probably no problem with his angular code since most of the CORS problams are only because of the server config – sam Dec 16 '14 at 12:08
  • I have the same kind of setup, I noticed that when I request a non-existing action on the API, and WebApi is returning a 404, the CORS header is missing and the browser will complain. So, maybe it is as simple as that. – Robin van der Knaap Dec 16 '14 at 12:11

17 Answers17

329

You need to enable CORS in your Web Api. The easier and preferred way to enable CORS globally is to add the following into web.config

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="*" />
      <add name="Access-Control-Allow-Headers" value="Content-Type" />
      <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

Please note that the Methods are all individually specified, instead of using *. This is because there is a bug occurring when using *.

You can also enable CORS by code.

Update
The following NuGet package is required: Microsoft.AspNet.WebApi.Cors.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.EnableCors();

        // ...
    }
}

Then you can use the [EnableCors] attribute on Actions or Controllers like this

[EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]

Or you can register it globally

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute("http://www.example.com", "*", "*");
        config.EnableCors(cors);

        // ...
    }
}

You also need to handle the preflight Options requests with HTTP OPTIONS requests.

Web API needs to respond to the Options request in order to confirm that it is indeed configured to support CORS.

To handle this, all you need to do is send an empty response back. You can do this inside your actions, or you can do it globally like this:

# Global.asax.cs
protected void Application_BeginRequest()
{
    if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
    {
        Response.Flush();
    }
}

This extra check was added to ensure that old APIs that were designed to accept only GET and POST requests will not be exploited. Imagine sending a DELETE request to an API designed when this verb didn't exist. The outcome is unpredictable and the results might be dangerous.

Mihai Dinculescu
  • 19,743
  • 8
  • 55
  • 70
  • 6
    Your answer helped me. I had tried everything I could with the code base solutions. I didn't try the web.config option until I read your answer. It was the only one that worked. Any idea why? I am using Web API 2 with OData. Thanks anyway! :) – Felipe Correa Jun 01 '15 at 06:08
  • Thanks. Where does this code go? I'm in web api 2 and I don't have an `Application_BeginRequest` – toddmo Aug 31 '15 at 14:53
  • It goes into `Global.asax.cs`. – Mihai Dinculescu Aug 31 '15 at 15:19
  • What if the page is supposed to be https? Would this still work or some tweaking required? – Nirav Zaveri Sep 30 '15 at 09:02
  • CORS should not care about SSL. – Mihai Dinculescu Sep 30 '15 at 19:32
  • Not finding the `EnableCors` or `EnableCorsAttribute` classes in any of my loaded assemblies -- what reference / NuGet package do I need to add to my project to make this work? – BrainSlugs83 Jan 15 '16 at 07:22
  • 1
    For future reference, the NuGet package you need for this is "Microsoft.AspNet.WebApi.Cors". – BrainSlugs83 Jan 15 '16 at 07:30
  • so so so much appreciated for this answer, easily got my answer and solution :) – 3 rules May 10 '16 at 10:10
  • 2
    I have followed all your answer and I have two questions: Where should Application_BeginRequest() be invoked? and second, in the same method, the .Contains("Origin") doesn't really compiles at me, where is this method from, String.Contains, or from Linq.Contains? – meJustAndrew Jun 19 '16 at 09:39
  • This was very helpful. One benefit to using code is that you can open up select API methods instead of enabling access to your entire library. – Laurie Dickinson Jul 25 '16 at 19:10
  • This is working for webapi aax calls, but not for static html files.In this case, the OPTIONS request headers look exactly the same, but for some reason the response has an Http Status Code of 405, not 200. Do you have a solution that works for this too? – Simon Green Aug 08 '16 at 17:04
  • It looks like your MVC handler doesn't deal with static files. You might need to configure a separate handler for those files. Have a look at http://stackoverflow.com/questions/22672541/enable-cors-for-static-resources-in-asp-net-mvc – Mihai Dinculescu Aug 08 '16 at 17:15
  • 2
    remember a different port # constitutes a different domain , which can be a gotcha . foo.com _is different domain_ than foo.com:8080 – RyBolt Aug 11 '16 at 17:02
  • the web.config entries worked for me. I also removed the from the same section I placed the other options. Thanks!! – hewstone Mar 24 '17 at 03:23
  • The Web.config entries were also all I needed. The WebAPI invokes that were failing with "No 'Access-Control-Allow-Origin' header is present" immediately started working for me after putting in the entries specified above. – StackOverflowUser May 11 '17 at 07:06
  • The web.config update solution mentioned here did not work in my case. – user1451111 Dec 04 '17 at 03:46
  • Previously using `[EnableCors(origins: "*", headers: "*", methods: "*")]`on each of my controller, the `accept` header was missing in Access-Control-Allow-Headers. I switched to registering globally (`EnableCorsAttribute("*", "*", "*"); config.EnableCors(cors);`) and it works fine now. – XouDo Sep 01 '20 at 07:32
  • I would add the following official documentation from Microsoft regarding CORS. It helped with my problem, had to add all the required handlers in the webserver section of the web.config: https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api – Lenard Bartha Mar 09 '23 at 17:55
31

@Mihai-Andrei Dinculescu's answer is correct, but for the benefit of searchers, there is also a subtle point that can cause this error.

Adding a '/' on the end of your URL will stop EnableCors from working in all instances (e.g. from the homepage).

I.e. This will not work

var cors = new EnableCorsAttribute("http://testing.azurewebsites.net/", "*", "*");
config.EnableCors(cors);

but this will work:

var cors = new EnableCorsAttribute("http://testing.azurewebsites.net", "*", "*");
config.EnableCors(cors);

The effect is the same if using the EnableCors Attribute.

JsAndDotNet
  • 16,260
  • 18
  • 100
  • 123
29

I followed all the steps above indicated by Mihai-Andrei Dinculescu.
But in my case, I needed 1 more step because http OPTIONS was disabled in the Web.Config by the line below.

<remove name="OPTIONSVerbHandler" />

I just removed it from Web.Config (just comment it like below) and Cors works like a charm

<handlers>
  <!-- remove name="OPTIONSVerbHandler" / -->
</handlers>
AlbertSY
  • 591
  • 5
  • 7
10

It may be because of the installation of Cors nuget packages.

If you facing the problem after installing and enabaling cors from nuget , then you may try reinstalling web Api.

From the package manager, run Update-Package Microsoft.AspNet.WebApi -reinstall

Bimal Das
  • 1,882
  • 5
  • 28
  • 53
  • 1
    This was exactly it for me. I installed System.Web.Http.Cors and then uninstalled, which left WebApi on the wrong (newly upgraded) version between 5.2.2 and 5.2.3 – TaeKwonJoe Sep 10 '16 at 01:26
  • Glad I saw this when I was setting up cors initially. Later on, I uninstalled a package that had nothing to do with cors and it left WebAPI cors in a bad state. – Michael Oct 21 '20 at 21:33
7

Try this, to make sure you configured CORS correctly:

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

Still not working? Check HTTP headers presence.

Andrei
  • 42,814
  • 35
  • 154
  • 218
  • to check if its working, its better to remove supportCredentials as well, it does disable cors in certain conditions – harishr Dec 16 '14 at 12:19
  • 2
    Best answer because I don't want to enable CORS for my whole site, just certain endpoints. `config.EnableCors()` is needed for this as well. – Csaba Toth Apr 19 '17 at 03:43
6

I know I'm coming to this very late. However, for anyone who's searching, I thought I'd publish what FINALLY worked for me. I'm not claiming it's the best solution - only that it worked.

Our WebApi service uses the config.EnableCors(corsAttribute) method. However, even with that, it would still fail on the pre-flight requests. @Mihai-Andrei Dinculescu's answer provided the clue for me. First of all, I added his Application_BeginRequest() code to flush the options requests. That STILL didn't work for me. The issue is that WebAPI still wasn't adding any of the expected headers to the OPTIONS request. Flushing it alone didn't work - but it gave me an idea. I added the custom headers that would otherwise be added via the web.config to the response for the OPTIONS request. Here's my code:

protected void Application_BeginRequest()
{
  if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
  {
    Response.Headers.Add("Access-Control-Allow-Origin", "https://localhost:44343");
    Response.Headers.Add("Access-Control-Allow-Headers",
      "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
    Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
    Response.Headers.Add("Access-Control-Allow-Credentials", "true");
    Response.Flush();
  }
}

Obviously, this only applies to the OPTIONS requests. All other verbs are handled by the CORS configuration. If there's a better approach to this, I'm all ears. It feels like a cheat to me and I would prefer if the headers were added automatically, but this is what finally worked and allowed me to move on.

John Groft
  • 61
  • 1
  • 1
  • You are late but this helped me solve a legacy issue by adding these as http headers in the web config. Thanks – Kell Aug 27 '21 at 09:33
  • I am getting below error : Access to XMLHttpRequest at 'https://portaluat.mab.in/mabi/api/Authentication/' from origin 'https://portaluat.mab.in:8443' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status. – Raju Gaddam Mar 22 '23 at 16:37
4

To make any CORS protocol to work, you need to have a OPTIONS method on every endpoint (or a global filter with this method) that will return those headers :

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: content-type

The reason is that the browser will send first an OPTIONS request to 'test' your server and see the authorizations

sam
  • 3,441
  • 2
  • 33
  • 42
4

That problem happens when you try to access from a different domain or different port.

If you're using Visual Studio, then go to Tools > NuGet Package Manager > Package Manager Console. There you have to install the NuGet Package Microsoft.AspNet.WebApi.Cors

Install-Package Microsoft.AspNet.WebApi.Cors

Then, in PROJECT > App_Start > WebApiConfig, enable CORS

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        
        //Enable CORS. Note that the domain doesn't have / in the end.
        config.EnableCors(new EnableCorsAttribute("https://tiagoperes.eu",headers:"*",methods:"*"));

        ....

    }
}

Once installed successfully, build the solution and that should suffice

Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
3

I catch the next case about cors. Maybe it will be useful to somebody. If you add feature 'WebDav Redirector' to your server, PUT and DELETE requests are failed.

So, you will need to remove 'WebDAVModule' from your IIS server:

  • "In the IIS modules Configuration, loop up the WebDAVModule, if your web server has it, then remove it".

Or add to your config:

<system.webServer>
<modules>
  <remove name="WebDAVModule"/>
</modules>
<handlers>
  <remove name="WebDAV" />
  ...
</handlers>

Andrey Ravkov
  • 1,268
  • 12
  • 21
1

@Mihai-Andrei Dinculescu's answer worked for me, e.g.:

  • Adding a <httpProtocol>in the web.config's <system.webServer> section
  • Returning empty response for OPTIONS requests via the mentioned Application_BeginRequest() in global.asax

Except that his check for Request.Headers.AllKeys.Contains("Origin") did NOT work for me, because the request contained an origing, so with lowercase. I think my browser (Chrome) sends it like this for CORS requests.

I solved this a bit more generically by using a case insensitive variant of his Contains check instead: if (culture.CompareInfo.IndexOf(string.Join(",", Request.Headers.AllKeys), "Origin", CompareOptions.IgnoreCase) >= 0) {

Community
  • 1
  • 1
Bart
  • 5,065
  • 1
  • 35
  • 43
0

I had tried everything I could find on the net including the methods that have been given on this answer. After almost trying to solve the problem for whole day I have found the solution that have worked for me like a charm.

in the file WebApiConfig in folder App_Start, comment all the lines of code and add the following code:

`public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        config.EnableCors();
        var enableCorsAttribute = new EnableCorsAttribute("*",
                                           "Origin, Content-Type, Accept",
                                           "GET, PUT, POST, DELETE, OPTIONS");
        config.EnableCors(enableCorsAttribute);
        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            //routeTemplate: "api/{controller}/{id}",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
        config.Formatters.Add(new BrowserJsonFormatter());
    }

    public class BrowserJsonFormatter : JsonMediaTypeFormatter
    {
        public BrowserJsonFormatter()
        {
            this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
            this.SerializerSettings.Formatting = Formatting.Indented;
        }

        public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
        {
            base.SetDefaultContentHeaders(type, headers, mediaType);
            headers.ContentType = new MediaTypeHeaderValue("application/json");
        }
    }`
Yagnesh Khamar
  • 184
  • 3
  • 8
0

I know that people will probably find this very obvious at first, but really think about this. This can often happen if you've done something wrong.

For instance, I've had this problem because I didn't add a host entry to my hosts file. The real problem was DNS resolution. Or I just got the base URL wrong.

Sometimes I get this error if the identity token came from one server, but I'm trying to use it on another.

Sometimes you'll get this error if you've got the resource wrong.

You might get this if you put the CORS middleware too late in the chain.

Bluebaron
  • 2,289
  • 2
  • 27
  • 37
0

Avoid multiple place enabling CORS,Like WebApiCOnfig.cs, GrantResourceOwnerCredentials method in provider and Controller Header attribute etc. Below are the list which also cause the Access Control Allow Origin

  1. Web having truble in interacting with DB which you used.
  2. AWS Cloud If VPC of Web API and DB are different.

Below code is more then enough to fix the access control allow origin. //Make sure app.UseCors should be top of the code line of configuration.

   public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            //All other configurations
        }
    }

This slowed my problem.

Sharad
  • 1,192
  • 1
  • 8
  • 20
0

Install package : Microsoft.AspNet.WebApi.Cors

go to : App_Start --> WebApiConfig

Add :

var cors = new EnableCorsAttribute("http://localhost:4200", "", ""); config.EnableCors(cors);

Note : If you add '/' as end of the particular url not worked for me.

B.Nishan
  • 536
  • 4
  • 13
0

I know this sounds crazy but for me it was [HttpPost()] instead of [HttpPost]. When I removed the redundant parentheses, it started working

Maccurt
  • 12,655
  • 7
  • 32
  • 43
-1

If you have security\requestFiltering nodes in your web.config as follows:

<security>
  <requestFiltering>
    <verbs allowUnlisted="false">
      <add verb="GET" allowed="true" />
      <add verb="POST" allowed="true" />
      <add verb="PUT" allowed="true" />
      <add verb="DELETE" allowed="true" />
      <add verb="DEBUG" allowed="true" />          
    </verbs>
  </requestFiltering>

make sure you add this as well

<add verb="OPTIONS" allowed="true" />
ozz
  • 5,098
  • 1
  • 50
  • 73
-1

For people that are simply creating a Web API in .NET 5 and not a Web App, in Startup.cs you'll need to configure your policy like so:

public void ConfigureServices(IServiceCollection services)
{
    // Additional configs above...
    services.AddCors(options =>
    {
        options.AddPolicy("AllowAnyOrigin", builder =>
        {
            // Allow "Access-Control-Allow-Origin: *" header
            builder.AllowAnyOrigin();
        });
    });
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Add this above other config options...
    app.UseCors("AllowAnyOrigin");
}
Simple Sandman
  • 900
  • 3
  • 11
  • 34