Cross-Origin Requests (CORS) in ASP.NET Core Web API

Cross-Origin Requests (CORS) in ASP.NET Core Web API

This blog is going to explain what a Cross Origin Request (CORS) is and how to implement it in ASP.NET Core web API. When a web page tries to access resources from a different webpage it is blocked by the browser security feature. This feature is called the Same Origin Policy (SOP). In many cases the developer has to access resources of different origins. In such cases CORS helps to relax the Same Origin Policy.

There are 3 ways to enable CORS in ASP.NET core.

  1. Middleware (name policy or default policy)
  2. Endpoint routing
  3. [EnableCORS] attribute

Let’s see that with the example.

Imagine you have an API which returns some JSON values. It’s URL is https://localhost:44351/api/Home. When trying to access another domain URL(https: // localhost: 44389 /) from the client page, it will give you the following error message.

Access to XMLHttpRequest at 'https://localhost:44351/api/Home' from origin 'https://localhost:44389' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

CORS ERROR

It happens because of the Same Origin Policy(SOP). To relax this same origin policy, you have to enable the Cross-Origin Requests (CORS) in the API. Let’s look at it one by one

Middleware

Default policy

To enable the CORS default policy, first you have to add the AddCors extension in the Startup.cs ConfigureServices() method in your API.

In the below code you can see that AddCors() is added to the service. And in the option, the default policy has been added. In the builder "https://localhost:44389" the URL has been whitelisted.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddCors(
        options => options.AddDefaultPolicy(
                builder => builder.WithOrigins("https://localhost:44389")
                .AllowAnyMethod()
                .AllowAnyHeader()
        ));
}

Next you need to add app.UseCors() middleware to the Configure() method. You must add CORS middleware before the UseRouting() middleware and after the UseAuthorization() middleware.

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

    app.UseRouting();

    app.UseCors();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Preflight

For some CORS requests, the browser first sends the preflight request to the API. The preflight request will contain the metadata of the original request. So the API server checks the metadata and responds to the priority request, declaring whether or not it can send a response to the original request.

The following is a original request header

Original Header Request
authority: localhost:44351
:method: GET
:path: /api/Home?_=1626359605790
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
content-type: application/json; charset=utf-8
origin: https://localhost:44389
referer: https://localhost:44389/
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
sec-ch-ua-mobile: ?0
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36

Original Response
In the response you can see the access-control-allow-origin: https://localhost:44389. This means the URL is whitelisted.

access-control-allow-origin: https://localhost:44389
content-length: 19
content-type: application/json; charset=utf-8
date: Thu, 15 Jul 2021 14:36:13 GMT
server: Microsoft-IIS/10.0
x-powered-by: ASP.NET

Named Policy

Another method of enabling CORS via middleware is the named policy. In the below code you can see that the name ‘SamplePolicy’ has been added to the AddPolicy() method and added the "https://localhost:44389" URL to the WithOrigins() method.
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddCors(
        options => options.AddPolicy("SamplePolicy",
        builder => builder.WithOrigins("https://localhost:44389")
        .AllowAnyHeader()
        .AllowAnyMethod())
        );
}

In the configure method, you have to pass the policy name "SamplePolicy" as a parameter in the app.UseCors() middleware. So it will enable the CORS.

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

    app.UseRouting();

    app.UseCors("SamplePolicy");

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Endpoint Routing

With endpoint routing you can enable the CORS to different endpoints using the RequireCors() extension.

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

    app.UseRouting();

    app.UseCors();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
          endpoints.MapControllers().RequireCors("SamplePolicy");
    });
}

Enable CORS with attributes

The following is an example of how to enable CORS using the EnableCors attribute. Here the attribute is added at the Controller level. It therefore enables CORS for all methods within the system.

[EnableCors("SamplePolicy")]
[Route("api/[controller]")]
[ApiController]
public class HomeController : Controller
{
    [HttpGet]
    public List<string> Get()
    {
        return new() { "value1", "value2"};
    }
}

Also, you can enable CORS at the method level also. So, You can add different CORS policies for each method.

[Route("api/[controller]")]
[ApiController]
public class HomeController : Controller
{
   [EnableCors("SamplePolicy1")]
    [HttpGet]
    public List<string> Get()
    {
        return new() { "value1", "value2"};
    }

    [EnableCors("SamplePolicy2")]
    [HttpGet]
    public List<string> GetValue()
    {
        return new() { "value3", "value4"};
    }
}

I hope this helps you. Keep coding.

Comments

Popular posts from this blog

Entity Framework Core (EF) with SQL Server LocalDB

Creating a C# Azure Function with Visual Studio: Step-by-Step Guide

Exploring EventCallback in Blazor: Building Interactive Components