3

I created an ASP.Net CORE web API project, with a single controller, and would now like to call it from a client (React) web app.

However, the call fails with "No 'Access-Control-Allow-Origin' header is present on the requested resource.".

When calling the same endpoint from Fiddler, the expected response headers are not present.

Thanks to ATerry, I have further insight: the headers are not present, because the React web app and the .Net Core web API are hosted on the same box. React populates the request Origin: header which is the same as the (API) box, thus the server (being really clever about it) does not add the Allow-... response headers. However, the React app rejects the response, because of the lack of those headers.

I'm using .Net Core v2.1 (latest as of this writing).

I built the code based on

https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1

I checked these

https://weblog.west-wind.com/posts/2016/Sep/26/ASPNET-Core-and-CORS-Gotchas

CORS in .NET Core

How to enable CORS in ASP.NET Core

... but none of the suggestions worked.

Any ideas?

This is how I configure the .Net Core app (code changed from actual to try and allow anything):

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Enable CORS (Cross Origin Requests) so that the React app on a different URL can access it
        // See https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1
        services.AddCors(options =>
        {
            options.AddPolicy(Global.CORS_ALLOW_ALL_POLICY_NAME, builder => builder
                .AllowAnyOrigin()
                .AllowAnyHeader()
                .AllowAnyMethod()
                .AllowCredentials());
        });

        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseCors(Global.CORS_ALLOW_ALL_POLICY_NAME);

        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

Having failed with just the above, I added the CORS attributes to the controller class and controller methods too:

[Route("api/[controller]")]
[ApiController]
[EnableCors(Global.CORS_ALLOW_ALL_POLICY_NAME)]
public class DealsController : ControllerBase
{
[...]
[HttpGet]
    [EnableCors(Global.CORS_ALLOW_ALL_POLICY_NAME)]
    public ActionResult<List<Deal>> GetAll()
    {
        return Store;
    }
}

The response headers I get:

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Server: Kestrel
X-Powered-By: ASP.NET
Date: Thu, 06 Sep 2018 12:23:27 GMT

The missing headers are:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:3000
balintn
  • 988
  • 1
  • 9
  • 19
  • 1
    Did you set the `origin` header in your request to be something other than your current domain? The API is smart enough to exclude the headers if it isn't a cross-domain request. – Alex Terry Sep 06 '18 at 12:51
  • Great observation, thanks. In the test, I initiated the request from Fiddller on the server box. When adding the request header "origin: somethingelse", the response does have the expected headers. And I just tested it from another box in the same subnet, with the same results: when calling without the origin request header, the Access-Control-Allow-... response headers are missing, when I add the request header they are correctly returned. – balintn Sep 06 '18 at 13:09
  • CORS is a two way confirmation, the Client needs to request CORS, and the Server needs to accept it – Richard Hubley Sep 06 '18 at 13:36
  • @Richard Hubley Could you give me a pointer on how to do that? I found a number of similar cases with other server types (like this one with a node js server describing just my problem: https://elegantcode.com/2018/06/10/cors-your-dev-environment-for-node-js-and-react-js/), but they all mention server side resolutions: the server has to provide the appropriate response headers. Nothing about clients. – balintn Sep 06 '18 at 13:51
  • @balintn What are you using as a client? looks like you have solved the problem from postman, by adding the header. Which client are you still having a problem with? – Richard Hubley Sep 06 '18 at 13:55
  • @Richard Hubley Yes, managed to solve it by changing the URL used to access the server from a localhost based one to an IP address based one (http://localhost/api to http://192.168.1.96/api). It seems that part of the filtering that ATerry mentioned is based on host name: IIS doesn't send the Allow-... headers if hostname is localhost. Trouble is that React requires them. – balintn Sep 08 '18 at 16:30
  • You should add that answer, it would be useful to others. And add the react tag – Richard Hubley Sep 09 '18 at 20:39

4 Answers4

6

I believe it should work fine with LOCALHOST hosting as well, just do below changes and remove and any extra changes/configurations.

Replace this:

        // Enable CORS (Cross Origin Requests) so that the React app on a different URL can access it
    // See https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1
    services.AddCors(options =>
    {
        options.AddPolicy(Global.CORS_ALLOW_ALL_POLICY_NAME, builder => builder
            .AllowAnyOrigin()
            .AllowAnyHeader()
            .AllowAnyMethod()
            .AllowCredentials());
    });

with this:

services.AddCors();

and Replace this:

app.UseCors(Global.CORS_ALLOW_ALL_POLICY_NAME);

with this:

app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());

NOTE:

  • Even if your Web Api and React app are configured on LOCALHOST doesn't mean they are from same origin, it is because they are hosted on different port like react app is hosted on LOCALHOST:3000 and Web Api is hosted on LOCALHOST:5000. Web api will complaint if client(react app) is requesting from different port.

  • Above Web Api code will allow ANY ORIGIN and in production applications this is not safe so you need to allow specific ORIGIN to CORS access.

1

Managed to solve it by changing the URL used to access the server from a localhost based one to an IP address based one (localhost/api to 192.168.1.96/api).

It seems that part of the filtering that ATerry mentioned is based on host name: IIS doesn't send the Allow-... headers if hostname is localhost. Trouble is that React requires them.

balintn
  • 988
  • 1
  • 9
  • 19
1

You could try something like below as explained here: https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.2

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy("AllowSpecificOrigin",
            builder => builder.WithOrigins("http://example.com"));
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, 
    ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    // Shows UseCors with named policy.
    app.UseCors("AllowSpecificOrigin");

    app.Run(async (context) =>
    {
        await context.Response.WriteAsync("Hello World!");
    });
}

In your scenario it could be changed to something like the code below.

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {

        services.AddCors(options => options.AddPolicy(Global.CORS_ALLOW_ALL_POLICY_NAME,
                builder =>
                {
                    builder.AllowAnyOrigin()
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials();
                }));

        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseCors(Global.CORS_ALLOW_ALL_POLICY_NAME);

        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

This code might not look any different from yours however, there is a slight difference in the way the actions(what you call the builder) are defined. I hope that helps, good luck! :)

0

I got stuck with this same issue recently but doubted if mine was CORS related. So I went to deploy the app to my local IIS to check if that will get resolved somehow. Then checked the logs and found an issue pertaining to circular reference in data models - "Self referencing loop detected for property..". Applied an action in Startup.js to resolve the issue like so,

    services.AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
                .AddJsonOptions(options =>
                {
                    options.SerializerSettings.Formatting = Formatting.Indented;

                    // this line
                    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                });
JamesCM
  • 73
  • 3
  • 6