Enable CORS on an ASP.NET Core API or MVC Website (C#)

This article requires ASP.NET Core.

It is very easy to enable CORS on an existing or new ASP.NET Core MVC or RESTful API project. See this quick guide if not familiar with what a REST API is.

First, modify Startup.cs as follows.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace EnableCorsForApi
{
    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)
        {


            // ################ THIS IS NEWLY ADDED ################
            services.AddCors(options =>
            {
                options.AddPolicy("WeatherForecastCorsPolicy", builder => // NOTE THE NAME OF THE POLICY HERE; IT IS POSSIBLE TO HAVE MORE THAN ONE POLICY
                {
                    builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
                });
            });


            services.AddControllers();
        }

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

            app.UseRouting();


            app.UseCors(); // ################ THIS IS NEWLY ADDED ################


            app.UseAuthorization();

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

Now, modify the controller action method that access should be granted to.

// WeatherForecastController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace EnableCorsForApi.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet]

        // USE THE POLICY NAME THAT WAS USED IN Startup.cs
        [EnableCors("WeatherForecastCorsPolicy")] // ################ THIS IS NEWLY ADDED ################

        public IEnumerable<WeatherForecast> Get()
        {
            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            });
        }
    }
}

Limiting Origins and Methods

The following Startup.cs file shows how to limit origins and methods. Do not forget to update the controller and/or controller action methods.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace EnableCorsForApi
{
	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("CorsPolicy1",
					builder =>
					{
						builder.WithOrigins("http://domain.com", "http://*.anotherdomain.com").AllowAnyHeader().AllowAnyMethod();
					});

				options.AddPolicy("CorsPolicy2",
					builder =>
					{
						builder.WithOrigins("http://www.example.com").AllowAnyHeader().WithMethods("POST","GET","PUT","DELETE");
					});
			});

			services.AddControllers();
		}

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

			app.UseRouting();


			app.UseCors(); // DON'T FORGET THIS LINE


			app.UseAuthorization();

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

Testing the Endpoint

Run the server and try accessing the method(s) that have CORS enabled. The following code can be used to access an API endpoint.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>JSON-MAN</title>
    <style>
        * {
            font-family: sans-serif;
            font-size: 30px;
            color: blue;
        }

        body {
            padding: 3%;
        }

        input, table, td:last-of-type {
            width: 100%;
        }

        textarea {
            width: 100%;
            height: 300px;
        }
    </style>
    <script type="text/javascript">
/*
request = {
	verb: "GET POST PUT PATCH DELETE",
	path: "/api/",
	headers: {"header1":"value1","header2":"value2"},
	data: "{'is':'json'}",
	onprogress: function(percent){}
};
*/
function ajax2(request) {
	var obj = "object";
	if (typeof request != obj) { request = {}; }
	var undef = "undefined";
	var canPromise = (typeof Promise != undef);
	var xmlobj;
	if (typeof XMLHttpRequest != undef) {
		xmlobj = new XMLHttpRequest();
	}
	else if (typeof window.ActiveXObject != undef) {
		var aVersions = ["MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp", "Microsoft.XMLHttp"];
		for (var i = 0; i < aVersions.length; i++) {
			try {
				xmlobj = new ActiveXObject(aVersions[i]);
				break;
			} catch (err) {
				//void
			}
		}
	}
	if (typeof xmlobj != obj) {
		return {then:function(){return{catch:function(ca){ca("XMLHttpRequest object could not be created");}}}};
	}
	if(typeof request.onprogress == "function" && typeof xmlobj.upload == obj) {
		xmlobj.upload.addEventListener("progress", function (event) {
			request.onprogress(Math.floor(event.loaded / event.total * 100));
		});
	}
	// if no verb is specified then use "get"; if no path is specified then use the current file
	xmlobj.open(request.verb || "get", request.path || location.pathname, canPromise);
	xmlobj.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
	if(typeof request.headers == obj) {
		for(var prop in request.headers) {
			xmlobj.setRequestHeader(prop, request.headers[prop]);
		}
	}
	xmlobj.send(request.data || null);
	if(canPromise) {
		return new Promise(function (resolve, reject) {
			xmlobj.onreadystatechange = function () {
				if (xmlobj.readyState == 4) {
					if (xmlobj.status >= 200 && xmlobj.status < 300) {
						resolve(xmlobj.responseText);
					}
					else {
						reject(xmlobj.responseText);
					}
				}
			};
		});
	}
	else {
		if (xmlobj.status >= 200 && xmlobj.status < 300) {
			return {then:function(th){th(xmlobj.responseText);return{catch:function(){}}}};
		}
		else {
			return {then:function(){return{catch:function(ca){ca(xmlobj.responseText);}}}};
		}
	}
}
var headersobj = null;
function setHeadersColor(input) {
	try {
		headersobj = JSON.parse(input.value);
		if (Array.isArray(headersobj)) {
			headersobj = null;
			input.style.color = "red";
		}
		else {
			input.style.color = "#0b0";
		}
	}
	catch {
		headersobj = null;
		input.style.color = "red";
	}
}
function setBodyColor(input) {
	try {
		JSON.parse(input.value);
		input.style.color = "#0b0";
	}
	catch {
		input.style.color = "red";
	}
}
function submitRequestForm(form) {
	ajax2({
		verb: form.requestmethod.value,
		path: form.endpoint.value,
		headers: headersobj,
		data: form.requestbody.value
	}).then(function (txt) {
		form.responsebody.value = txt;
		return false;
	}).catch(function (err) {
		alert("ERROR");
		form.reset();
		return false;
	});
	return false;
}
    </script>
</head>
<body>
    <h1>JSON-MAN</h1>
    <form method="get" action="" onsubmit="return submitRequestForm(this);">
        <div>
            <table><tr><td><select name="requestmethod"><option>GET</option><option>POST</option><option>PUT</option><option>PATCH</option><option>DELETE</option></select></td><td><input type="text" name="endpoint" placeholder="ENDPOINT" /></td></tr></table>
        </div>
        <div>
            <input type="text" name="headers" placeholder='HEADERS EXAMPLE: {"header1":"value1","header2":"value2"}' onchange="setHeadersColor(this);" onkeyup="setHeadersColor(this);" autocomplete="off" />
        </div>
        <div>
            <textarea name="requestbody" placeholder="REQUEST BODY" onchange="setBodyColor(this);" onkeyup="setBodyColor(this);"></textarea>
        </div>
        <div>
            <textarea name="responsebody" placeholder="RESPONSE BODY" readonly></textarea>
        </div>
        <div>
            <button type="submit">submit</button>
        </div>
    </form>
</body>
</html>

Coding Video

https://youtu.be/n4gnX54BHlQ