简体   繁体   中英

Can't enable CORS in ASP.Net Core web api

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://docs.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://docs.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

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://docs.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.

You could try something like below as explained here: https://docs.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! :)

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM