.NET Core Endpoint + Global CORS - c#

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.

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.

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

Related

.net core middleware and authorization

I have a custom middleware that is configured like this
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseWhen(context => GetCondition(context)
, appBuilder =>
{
appBuilder.UseAuthentication(); // this does not do the trick
appBuilder.UseAuthorization();// this does not do the trick
appBuilder.UseMiddleware<MyCustomMiddleware>();
});
}
I want my custom middleware to execute only when the user is authorized to do so.
If I am using controller, I can provide Authorize attribute on controller which forces the user to login or 401 if they are not logged-in.
[Authorize(AuthenticationSchemes = "Bearer")]
public class WeatherForecastController : ControllerBase
How can I do the similar authorization in middleware. One way to do is to check in the invoke method if user is authenticated but that does not seems right to me
public async Task Invoke(HttpContext context)
{
if (context.User.Identity.IsAuthenticated)
{
// my code goes here
}
}

Rewrite Route in middleware after Endpoint got matched

I am currently writing a rate-limiter with the help of a middleware, this is based on an per-user basis. When the rate-limit exceeds for a user, I want to rewrite the route to my 429 page which tells the use that he exceeded the rate-limit.
This is an example of what my current middle-ware looks like:
public class RateLimitMiddleware : IMiddleware
{
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if(RateLimitExceeded(context.User))
{
context.Request.Path = "/Home/Ratelimit";
}
return next.Invoke(context);
}
public bool RateLimitExceeded(ClaimsPrincipal user)
{
//...
}
}
Now to my Configure method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseRateLimit(); // This adds the RateLimitMiddleware class to the middlewares.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Now to the actual problem, you can't rewrite the route in any middleware after the UseRouting. Since it is the actual method deciding which Endpoint to pick. Therefore, is it possible to rewrite the route afterwards?
However, please do note, that I don't want to redirect the user to the rate-limit view. Additionally, I can't just move the middleware up in the chain, since I need the HttpContext.User to be set.

Terminal middleware (api key check) not working with endpoint routing

I am implementing an api where on one specific POST route there must be an API key check.
The SDK is dotnet core 3.1.
When I run the API using Postman, I get the following error:
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints
I looks like the route attribute on the api controller, together with endpoint route registration are stacked...
How can this be fixed ?
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
app.UseSerilogRequestLogging();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger(Configuration);
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapApiKeyAuthentication();
endpoints.MapControllers();
});
}
(extension class)
public static IEndpointRouteBuilder MapApiKeyAuthentication(this IEndpointRouteBuilder endpoints)
{
endpoints.MapPost("/v1/xyz", endpoints.CreateApplicationBuilder()
.UseMiddleware<ApiKeyMiddleware>()
.Build())
.WithDisplayName("ApiKey");
return endpoints;
}

Can't see react page after serving data with .net core api

I want to show data in my react component but when I set API react doesn't work. It seems like middleware is setup wrong or I don't get how .net core and react go along. I know I basically run two different apps with .net core and react but somebody could tell me how to resolve my problem to just put this API data in react page? I made it with approach model first using Entity framework.
I tried to change middleware but in the end, API is always shown. When I change the routing of API, react works fine so everything is good with set up to react. But when they are two on the same route. I only see data from API. And i only want to get data from .net core and show it in react component.
UsersController.cs
[Route("[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly MafiaContext _context;
public UsersController(MafiaContext context)
{
_context = context;
}
// GET: Users
[HttpGet]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
return await _context.Users.ToListAsync();
}
app.js
<Layout>
<Route exact path="/" component={Home} />
<Route path="/users" component={User} /> //this one is a route for the api
<Route path="/counter" component={Counter} />
<Route path="/fetch-data" component={FetchData} />
</Layout>
Startup.cs
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.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<MafiaContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DevConnection")));
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
// 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.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
}
I want to grab data that is shown in the picture and put it in the react table.

How to enable CORS in ASP.net Core WebAPI

What I am trying to do
I have a backend ASP.Net Core Web API hosted on an Azure Free Plan (Source Code: https://github.com/killerrin/Portfolio-Backend).
I also have a Client Website which I want to make consume that API. The Client Application will not be hosted on Azure, but rather will be hosted on Github Pages or on another Web Hosting Service that I have access to. Because of this the domain names won't line up.
Looking into this, I need to enable CORS on the Web API side, however I have tried just about everything for several hours now and it is refusing to work.
How I have the Client Setup
Its just a simple client written in React.js. I'm calling the APIs through AJAX in Jquery. The React site works so I know its not that. The Jquery API call works as I confirmed in Attempt 1. Here is how I make the calls
var apiUrl = "http://andrewgodfroyportfolioapi.azurewebsites.net/api/Authentication";
//alert(username + "|" + password + "|" + apiUrl);
$.ajax({
url: apiUrl,
type: "POST",
data: {
username: username,
password: password
},
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
var authenticatedUser = JSON.parse(response);
//alert("Data Loaded: " + authenticatedUser);
if (onComplete != null) {
onComplete(authenticatedUser);
}
},
error: function (xhr, status, error) {
//alert(xhr.responseText);
if (onComplete != null) {
onComplete(xhr.responseText);
}
}
});
What I have tried
Attempt 1 - The 'proper' way
https://learn.microsoft.com/en-us/aspnet/core/security/cors
I have followed this tutorial on the Microsoft Website to a T, trying all 3 options of enabling it Globally in the Startup.cs, Setting it up on every controller and Trying it on every Action.
Following this method, the Cross Domain works, but only on a single Action on a single controller (POST to the AccountController). For everything else, the Microsoft.AspNetCore.Cors middleware refuses to set the headers.
I installed Microsoft.AspNetCore.Cors through NUGET and the version is 1.1.2
Here is how I have it setup in Startup.cs
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add Cors
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
// Add framework services.
services.AddMvc();
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new CorsAuthorizationFilterFactory("MyPolicy"));
});
...
...
...
}
// This method gets called by the runtime. Use this method to configure
//the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
// Enable Cors
app.UseCors("MyPolicy");
//app.UseMvcWithDefaultRoute();
app.UseMvc();
...
...
...
}
As you can see, I am doing everything as told. I add Cors before MVC both times, and when that didn't work I attempted putting [EnableCors("MyPolicy")] on every controller as so
[Route("api/[controller]")]
[EnableCors("MyPolicy")]
public class AdminController : Controller
Attempt 2 - Brute Forcing it
https://andrewlock.net/adding-default-security-headers-in-asp-net-core/
After several hours of trying on the previous attempt, I figured I would try to bruteforce it by trying to set the headers manually, forcing them to run on every response. I did this following this tutorial on how to manually add headers to every response.
These are the headers I added
.AddCustomHeader("Access-Control-Allow-Origin", "*")
.AddCustomHeader("Access-Control-Allow-Methods", "*")
.AddCustomHeader("Access-Control-Allow-Headers", "*")
.AddCustomHeader("Access-Control-Max-Age", "86400")
These are other headers I tried which failed
.AddCustomHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE")
.AddCustomHeader("Access-Control-Allow-Headers", "content-type, accept, X-PINGOTHER")
.AddCustomHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Host, User-Agent, Accept, Accept: application/json, application/json, Accept-Language, Accept-Encoding, Access-Control-Request-Method, Access-Control-Request-Headers, Origin, Connection, Content-Type, Content-Type: application/json, Authorization, Connection, Origin, Referer")
With this method, the Cross Site headers are being properly applied and they show up in my developer console and in Postman. The problem however is that while it passes the Access-Control-Allow-Origin check, the webbrowser throws a hissy fit on (I believe) Access-Control-Allow-Headers stating 415 (Unsupported Media Type)
So the brute force method doesn't work either
Finally
Has anyone gotten this to work and could lend a hand, or just be able to point me in the right direction?
EDIT
So to get the API calls to go through, I had to stop using JQuery and switch to a Pure Javascript XMLHttpRequest format.
Attempt 1
I managed to get the Microsoft.AspNetCore.Cors to work by following MindingData's answer, except within the Configure Method putting the app.UseCors before app.UseMvc.
In addition, when mixed with the Javascript API Solution options.AllowAnyOrigin() for wildcard support began to work as well.
Attempt 2
So I have managed to get Attempt 2 (brute forcing it) to work... with the only exception that the Wildcard for Access-Control-Allow-Origin doesn't work and as such I have to manually set the domains that have access to it.
Its obviously not ideal since I just want this WebAPI to be wide opened to everyone, but it atleast works for me on a separate site, which means it's a start
app.UseSecurityHeadersMiddleware(new SecurityHeadersBuilder()
.AddDefaultSecurePolicy()
.AddCustomHeader("Access-Control-Allow-Origin", "http://localhost:3000")
.AddCustomHeader("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, PATCH, DELETE")
.AddCustomHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type, Authorization"));
Because you have a very simple CORS policy (Allow all requests from XXX domain), you don't need to make it so complicated. Try doing the following first (A very basic implementation of CORS).
If you haven't already, install the CORS nuget package.
Install-Package Microsoft.AspNetCore.Cors
In the ConfigureServices method of your startup.cs, add the CORS services.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(); // Make sure you call this previous to AddMvc
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
Then in your Configure method of your startup.cs, add the following :
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Make sure you call this before calling app.UseMvc()
app.UseCors(
options => options.WithOrigins("http://example.com").AllowAnyMethod()
);
app.UseMvc();
}
Now give it a go. Policies are for when you want different policies for different actions (e.g. different hosts or different headers). For your simple example you really don't need it. Start with this simple example and tweak as you need to from there.
Further reading : http://dotnetcoretutorials.com/2017/01/03/enabling-cors-asp-net-core/
In ConfigureServices add services.AddCors(); BEFORE services.AddMvc();
Add UseCors in Configure
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseMvc();
Main point is that add app.UseCors, before app.UseMvc().
Make sure you declare the CORS functionality before MVC so the middleware fires before the MVC pipeline gets control and terminates the request.
After the above method works you can change it configure a specific ORIGIN to accept api calls and avoid leaving your API so open to anyone
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("ApiCorsPolicy", builder =>
{
builder.WithOrigins("http://localhost:4200").AllowAnyMethod().AllowAnyHeader();
}));
services.AddMvc();
}
In the configure method tell CORS to use the policy you just created:
app.UseCors("ApiCorsPolicy");
app.UseMvc();
I just found this compact article on the subject -
https://dzone.com/articles/cors-in-net-core-net-core-security-part-vi
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors(builder => builder
.AllowAnyHeader()
.AllowAnyMethod()
.SetIsOriginAllowed((host) => true)
.AllowCredentials()
);
}
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
}
I created my own middleware class that worked for me, i think there is something wrong with .net core middleware class
public class CorsMiddleware
{
private readonly RequestDelegate _next;
public CorsMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext httpContext)
{
httpContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");
httpContext.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
httpContext.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
httpContext.Response.Headers.Add("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
return _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class CorsMiddlewareExtensions
{
public static IApplicationBuilder UseCorsMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CorsMiddleware>();
}
}
and used it this way in the startup.cs
app.UseCorsMiddleware();
I was struggling with this for DAYS.
I finally got it to work by moving app.UseCors(CORS_POLICY); to the TOP of Configure().
https://weblog.west-wind.com/posts/2016/sep/26/aspnet-core-and-cors-gotchas
Make sure you declare the CORS functionality before > MVC as the
headers have to be applied before MVC completes the request.
<= Even though my app didn't call UseMVC(), moving UseCors() to the top fixed the problem
Also:
Microsoft.AspNetCore.Cors used to be a required NuGet package in .Net Core 2 and lower; it's now automatically a part of Microsoft.AspNetCore in .Net Core 3 and higher.
builder.AllowAnyOrigin() and .AllowCredentials() CORS options are now mutually exclusive in .Net Core 3 and higher
CORS policy seems to require Angular call the server with https. An http URL seemed to give a CORS error regardless of the .Net Core server's CORS configuration. For example, http://localhost:52774/api/Contacts would give a CORS error; simply changing the URL to https://localhost:44333/api/Contacts worked.
Additional note:
In my case, CORS wouldn't work until I moved app.UseCors() above app.UseEndpoints(endpoints => endpoints.MapControllers()).
In my case only get request works well according to MindingData's answer. For other types of request you need to write:
app.UseCors(corsPolicyBuilder =>
corsPolicyBuilder.WithOrigins("http://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader()
);
Don't forget to add .AllowAnyHeader()
For .NET CORE 3.1
In my case, I was using https redirection just before adding cors middleware and able to fix the issue by changing order of them
What i mean is:
change this:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseHttpsRedirection();
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
...
}
to this:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseHttpsRedirection();
...
}
By the way, allowing requests from any origins and methods may not be a good idea for production stage, you should write your own cors policies at production.
To expand on user8266077's answer, I found that I still needed to supply OPTIONS response for preflight requests in .NET Core 2.1-preview for my use case:
// https://stackoverflow.com/a/45844400
public class CorsMiddleware
{
private readonly RequestDelegate _next;
public CorsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
context.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
// Added "Accept-Encoding" to this list
context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Accept-Encoding, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
context.Response.Headers.Add("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
// New Code Starts here
if (context.Request.Method == "OPTIONS")
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
await context.Response.WriteAsync(string.Empty);
}
// New Code Ends here
await _next(context);
}
}
and then enabled the middleware like so in Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware(typeof(CorsMiddleware));
// ... other middleware inclusion such as ErrorHandling, Caching, etc
app.UseMvc();
}
for ASP.NET Core 3.1 this soleved my Problem
https://jasonwatmore.com/post/2020/05/20/aspnet-core-api-allow-cors-requests-from-any-origin-and-with-credentials
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();
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)
{
app.UseRouting();
// global cors policy
app.UseCors(x => x
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(origin => true) // allow any origin
.AllowCredentials()); // allow credentials
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(x => x.MapControllers());
}
}
For .Net Core 6
var builder = WebApplication.CreateBuilder(args);
var apiCorsPolicy = "ApiCorsPolicy";
builder.Services.AddCors(options =>
{
options.AddPolicy(name: apiCorsPolicy,
builder =>
{
builder.WithOrigins("http://localhost:4200", "https://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
//.WithMethods("OPTIONS", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseCors(apiCorsPolicy);
app.UseAuthorization();
app.MapControllers();
app.Run();
here more examples
None of the above procedures helped and I then read article which solved the issue.
Below is the code.
public void ConfigureServices(IServiceCollection services)
{
// Add service and create Policy with options
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials() );
});
services.AddMvc();
}
and
public void Configure(IApplicationBuilder app)
{
// ...
// global policy - assign here or on each controller
app.UseCors("CorsPolicy");
and on the top of my actionmethod
[EnableCors("CorsPolicy")]
Simplest solution is add
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors(options => options.AllowAnyOrigin());
app.UseHttpsRedirection();
app.UseMvc();
}
to Startup.cs.
try adding jQuery.support.cors = true; before the Ajax call
It could also be that the data your sending to the API is wonky,
try adding the following JSON function
var JSON = JSON || {};
// implement JSON.stringify serialization
JSON.stringify = JSON.stringify || function (obj) {
var t = typeof (obj);
if (t != "object" || obj === null) {
// simple data type
if (t == "string") obj = '"' + obj + '"';
return String(obj);
}
else {
// recurse array or object
var n, v, json = [], arr = (obj && obj.constructor == Array);
for (n in obj) {
v = obj[n]; t = typeof (v);
if (t == "string") v = '"' + v + '"';
else if (t == "object" && v !== null) v = JSON.stringify(v);
json.push((arr ? "" : '"' + n + '":') + String(v));
}
return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
}
};
// implement JSON.parse de-serialization
JSON.parse = JSON.parse || function (str) {
if (str === "") str = '""';
eval("var p=" + str + ";");
return p;
};
then in your data: object change it to
data: JSON.stringify({
username: username,
password: password
}),
I think if you use your own CORS middleware you need to make sure it is really CORS request by checking origin header.
public class CorsMiddleware
{
private readonly RequestDelegate _next;
private readonly IMemoryCache _cache;
private readonly ILogger<CorsMiddleware> _logger;
public CorsMiddleware(RequestDelegate next, IMemoryCache cache, ILogger<CorsMiddleware> logger)
{
_next = next;
_cache = cache;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context, IAdministrationApi adminApi)
{
if (context.Request.Headers.ContainsKey(CorsConstants.Origin) || context.Request.Headers.ContainsKey("origin"))
{
if (!context.Request.Headers.TryGetValue(CorsConstants.Origin, out var origin))
{
context.Request.Headers.TryGetValue("origin", out origin);
}
bool isAllowed;
// Getting origin from DB to check with one from request and save it in cache
var result = _cache.GetOrCreateAsync(origin, async cacheEntry => await adminApi.DoesExistAsync(origin));
isAllowed = result.Result.Result;
if (isAllowed)
{
context.Response.Headers.Add(CorsConstants.AccessControlAllowOrigin, origin);
context.Response.Headers.Add(
CorsConstants.AccessControlAllowHeaders,
$"{HeaderNames.Authorization}, {HeaderNames.ContentType}, {HeaderNames.AcceptLanguage}, {HeaderNames.Accept}");
context.Response.Headers.Add(CorsConstants.AccessControlAllowMethods, "POST, GET, PUT, PATCH, DELETE, OPTIONS");
if (context.Request.Method == "OPTIONS")
{
_logger.LogInformation("CORS with origin {Origin} was handled successfully", origin);
context.Response.StatusCode = (int)HttpStatusCode.NoContent;
return;
}
await _next(context);
}
else
{
if (context.Request.Method == "OPTIONS")
{
_logger.LogInformation("Preflight CORS request with origin {Origin} was declined", origin);
context.Response.StatusCode = (int)HttpStatusCode.NoContent;
return;
}
_logger.LogInformation("Simple CORS request with origin {Origin} was declined", origin);
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return;
}
}
await _next(context);
}
For me, it had nothing to do with the code that I was using. For Azure we had to go into the settings of the App Service, on the side menu the entry "CORS". There I had to add the domain that I was requesting stuff from. Once I had that in, everything was magic.
For me the solution was to correct the order:
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
Here's a .NET 6 example of a Program.cs file using top-level statements to configure CORS. As can be seen, builder.Services.AddCors and app.UseCors are the required statements. The two commented UseCors statements also work and were included to show other options. I made no changes to my ASP.NET API controllers.
For reference, my development Angular app is running on localhost:4200 and is connecting to the development ASP.NET API server using https://localhost:7262.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
//app.UseCors(options => options.WithOrigins("http://localhost:4200").AllowAnyMethod());
//app.UseCors(options => options.WithOrigins("http://localhost:4200").WithMethods(new string[] {"POST", "PUT"}));
app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod());
app.MapControllers();
app.Run();
In launchSettings.json, under iisSettings, set anonymousAuthentication to true:
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:4200/",
"sslPort": 0
}
}
Then, in Startup.cs, under ConfigureServices, before services.AddMvc, add:
services.AddCors(options => options.AddPolicy("ApiCorsPolicy", builder =>
{
builder
.AllowAnyOrigin()
.WithHeaders(HeaderNames.AccessControlAllowHeaders, "Content-Type")
.AllowAnyMethod()
.AllowCredentials();
}));
and then, in configure method, before app.UseMvc() add:
app.UseCors("ApiCorsPolicy");
.NET Core 3.1
Worked for me and how the docs say to do it:
in Startup class:
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
In ConfigureServices() method:
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
In Configure() method:
app.UseCors(MyAllowSpecificOrigins);
https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.1
Based on your comment in MindingData's answer, it has nothing to do with your CORS, it's working fine.
Your Controller action is returning the wrong data. HttpCode 415 means, "Unsupported Media type". This happens when you either pass the wrong format to the controller (i.e. XML to a controller which only accepts json) or when you return a wrong type (return Xml in a controller which is declared to only return xml).
For later one check existence of [Produces("...")]attribute on your action
I'm using .Net CORE 3.1 and I spent ages banging my head against a wall with this one when I realised that my code has started actually working but my debugging environment was broken, so here's 2 hints if you're trying to troubleshoot the problem:
If you're trying to log response headers using ASP.NET middleware, the "Access-Control-Allow-Origin" header will never show up even if it's there. I don't know how but it seems to be added outside the pipeline (in the end I had to use wireshark to see it).
.NET CORE won't send the "Access-Control-Allow-Origin" in the response unless you have an "Origin" header in your request. Postman won't set this automatically so you'll need to add it yourself.
Here is my code : )
app.Use((ctx, next) =>
{
ctx.Response.Headers.Add("Access-Control-Allow-Origin", ctx.Request.Headers["Origin"]);
ctx.Response.Headers.Add("Access-Control-Allow-Methods", "*");
ctx.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
ctx.Response.Headers.Add("Access-Control-Allow-Headers", "AccessToken,Content-Type");
ctx.Response.Headers.Add("Access-Control-Expose-Headers", "*");
if (ctx.Request.Method.ToLower() == "options")
{
ctx.Response.StatusCode = 204;
return Task.CompletedTask;
}
return next();
});
In my case I fixed with UseCors before UserRouting..
Simple and easy way to do it.
Install package
Install-Package Microsoft.AspNetCore.Cors
Put this below code in startup.cs file
app.UseCors(options => options.AllowAnyOrigin());
Use a custom Action/Controller Attribute to set the CORS headers.
Example:
public class AllowMyRequestsAttribute : ControllerAttribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
// check origin
var origin = context.HttpContext.Request.Headers["origin"].FirstOrDefault();
if (origin == someValidOrigin)
{
context.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin", origin);
context.HttpContext.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
context.HttpContext.Response.Headers.Add("Access-Control-Allow-Headers", "*");
context.HttpContext.Response.Headers.Add("Access-Control-Allow-Methods", "*");
// Add whatever CORS Headers you need.
}
}
public void OnActionExecuting(ActionExecutingContext context)
{
// empty
}
}
Then on the Web API Controller / Action:
[ApiController]
[AllowMyRequests]
public class MyController : ApiController
{
[HttpGet]
public ActionResult<string> Get()
{
return "Hello World";
}
}
In my case character / at the end of my origin name was causing an issue.
Solution that worked out for me in .NET Core 3.1:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(c => c.AddPolicy("PolicyName", policy => {
policy.WithOrigins("http://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader();
}));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors("PolicyName");
}
The solution that worked for me in ASP.NET Core 3.1:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
services.AddControllersWithViews();
}
and then change the following:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors("CorsPolicy");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Then program worked and error was solved.
I got MindingData's answer above to work, but I had to use Microsoft.AspNet.Cors instead of Microsoft.AspNetCore.Cors. I am using .NetCore Web Application API project in Visual Studio 2019
The
Microsoft.AspNetCore.Cors
will allow you to do CORS with built-in features, but it does not handle OPTIONS request.
The best workaround so far is creating a new Middleware as suggested in a previous post. Check the answer marked as correct in the following post:
Enable OPTIONS header for CORS on .NET Core Web API
Here is how I did this.
I see that in some answers they are setting app.UserCors("xxxPloicy") and putting [EnableCors("xxxPloicy")] in controllers. You do not need to do both.
Here are the steps.
In Startup.cs inside the ConfigureServices add the following code.
services.AddCors(c=>c.AddPolicy("xxxPolicy",builder => {
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
If you want to apply all over the project then add the following code in Configure method in Startup.cs
app.UseCors("xxxPolicy");
Or
If you want to add it to the specific controllers then add enable cors code as shown below.
[EnableCors("xxxPolicy")]
[Route("api/[controller]")]
[ApiController]
public class TutorialController : ControllerBase {}
For more info: see this

Categories

Resources