zhuber
zhuber

Reputation: 5524

.NET Core Endpoint + Global CORS

I've found this in official documentation -

We recommend against combining policies. Use the [EnableCors] attribute or middleware, not both in the same app.

My scenario is quite simple - I want to enable CORS globally but disable it only for one specific controller endpoint (endpoint is used on frontend widget which can be embedded on any site so I can't have CORS on that endpoint).

I don't understand why they are recommending against combining both approaches - not only that they don't recommend but it just doesn't work.

This is the setup of CORS:

services.AddCors(opts =>
{
    opts.AddPolicy(nameof(MyCorsPolicy), new MyCorsPolicy());
});

And this is registration in Configure method of startup

public void Configure(
        IApplicationBuilder app,
        IWebHostEnvironment env)
{
    app.UseRouting();

    app.UseCors(nameof(MyCorsPolicy));
    
    app.UseHsts();
    
    app.UseExceptionHandler(env);

    app.UseAuthentication();
    app.UseAuthorization();
    
    app.UseEndpoints(endpoints => endpoints.MapControllers());
}

And now in my XY controller method I have [DisableCors] attribute which just doesn't work.

Any help would be appreciated. Thank you.

Upvotes: 0

Views: 1431

Answers (2)

zhuber
zhuber

Reputation: 5524

After hundreds of tests and internal .NET Core debugging, only way I could implement this is by using global CORS:

services.AddCors(opts =>
{
    opts.AddPolicy(nameof(MyCorsPolicy), new MyCorsPolicy());
});

Then I'd create another policy

public class AllowAnyCorsPolicy : CorsPolicy
{
    public AllowAnyCorsPolicy()
    {
        Origins.Clear();
        IsOriginAllowed = origin => true;
        Headers.Clear();
        Headers.Add("*");
        
        Methods.Clear();
        Methods.Add("*");

        SupportsCredentials = true;
    }
}

And apply that policy to specific controller method e.g.

[EnableCors(nameof(AllowAnyCorsPolicy))]
[HttpPost("/user/add")]
[AllowAnonymous]
public async Task<IActionResult> AddUser(UserRequestModel requestModel)
{
    // ...
}

If I used [DisableCors] or even used default policy registration and then added pure [EnableCors] attribute to controller method, it just wouldn't work. Pretty weird way of their implementation because I think this can be simplified a lot, and I have no idea how this might behave in future, so we might even consider writing our own full CORS middleware.

Upvotes: 2

Michael Wang
Michael Wang

Reputation: 4022

Way 1. Because a default policy hasn't been configured, app.UseCors() alone doesn't enable CORS. Use RequireCors to enable all controllers.

     public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseHttpsRedirection();
                app.UseStaticFiles();
                app.UseRouting();
    
                app.UseCors();
    
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
               {
                  endpoints.MapControllers()
                         .RequireCors(MyCorsPolicy);//Enable Cors with endpoint routing

                // /xy/getvalues2 and Razor Pages not allow cross-origin requests because no default policy was specified.
                  endpoints.MapGet("/xy/getvalues2",
                      context => context.Response.WriteAsync("xy/getvalues2")); //do XY Controller Action logic
                  endpoints.MapRazorPages(); 
           });
    }

Way 2. The [DisableCors] attribute does not disable CORS that has been enabled by endpoint routing. Uses [EnableCors("MyCorsPolicy")] to enable the "MyCorsPolicy" CORS policy for each controller. Disables CORS for the GetValues2 method.

    [EnableCors("MyCorsPolicy")]
    [Route("api/[controller]")]
    [ApiController]
    public class XYController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public IActionResult Get() =>
            ControllerContext.MyDisplayRouteInfo();

     // GET: api/values/GetValues2
        [DisableCors]
        [HttpGet("{action}")]
        public IActionResult GetValues2() =>
            ControllerContext.MyDisplayRouteInfo();

    }

Upvotes: 0

Related Questions