One of the most frustrating things about making an API or a front-end application is not bugs, but CORS. Nothing is uglier than seeing my browser’s Console telling me the application is not allowed to talk to my API because of CORS. But is it really a bad thing? Is it a menace that we should remove from the equation? I don’t think so. CORS in API is here to protect us, not to annoy us. So, what is CORS, how do we use it, and most importantly: how can we work with it?
Things you should know
This tutorial doesn’t require any special knowledge other than that you are a developer for web applications and come across CORS. I will be using a minimal API made in C# and .NET 7 to show and fix the “problem”.
Table Of Contents
What is CORS?
CORS stands for Cross-Origin Resource Sharing and was proposed in 2004 for allowing safe cross-origin data requests by VoiceXML browsers. It was in 2014 that it was a W3C Recommendation.
The idea behind CORS is simple: If a domain tries to access resources from another domain, the latter can reject the request because the request comes from a different domain. So, if https://www.google.com tries to access this website, this website might tell Google that it’s not allowed.
It’s a way of securing your web applications. CORS is mostly used for APIs. A website, like the one you are looking at right now, should be available for everyone. It would be a bit weird if I would just allow certain IP addresses to my website. An API is not really visible on the internet, not publicly. You generally don’t find API endpoints via Google nor will people cheer if you use them directly.
Therefore CORS is used when XMLHTTPRequests or Fetch APIs are invoked. Also, when requesting Web Fonts, CSS Shapes from images, and more.
This is all fun to know, but you are probably here because you ran into a problem with CORS.
The problem
To create the problem or error with CORS I use the minimal API I created in a previous tutorial and I will create an Angular application to activate a XMLHTTPRequest.
The API
You can download the API from GitHub if you want. It doesn’t have any fancy code. You can just open it in Visual Studio and run it.
This API has a few, simple endpoints. I will be using the first GET:
All it does is return a set of movies. That’s it! That’s part one of our case. Let’s start up the API.
Angular application
I am not explaining how to create an Angular application, so these are the steps I follow to create the application for this case.
- Create a new Angular application with the following command:
ng new CorsCase
It doesn’t matter if I use routing or which stylesheet format I select.
- In the app.component.ts I add the following code:
The constructor will be activated as soon as the component is rendered. It will do a call to my API to get all the movies.
Don’t forget to add the HttpClientModule to the app.module.ts! And the port of the API could be different for you.
Let’s start up the Angular application too.
The problem
Let’s browse to localhost:4200 and see what happens. I also open the browser’s console to see errors and it’s pretty red.
I think the problem is pretty clear. Let’s fix it.
The fixes
The errors we get are not really errors. It’s more a security to keep unwanted requests outside. It’s also not an error within the Angular application, so we can keep that running. No need to touch that for now. No, we need to let the API know that the request is valid.
Preparation
Before we can go to the fixes we need to add CORS to our API. We need to add it to our services and let the app use the CORS. This is crucial otherwise nothing will work.
First I add the CORS to the services of my API. I add the following line somewhere where builder.services is used in the Program.cs.
Then I add the following line of code under the mappings:
The app.UseCors() accepts a parameter with the type Action<CorsPolicyBuilder> configurePolicy. With this one we can configure what we want to accept and what not.
The CorsPolicyBuilder has all kinds of configurations on how to handle CORS. Take a look at the methods, these are the important part here.
But we will use this one in the next chapters.
You can start the API again and refresh the Angular application, but the error is still there. We now need to configure how we want to use the CORS.
The easy fix
If you are working with APIs for a while and have seen this error a lot, you might just go for the quick and easy fix: Just allow everything in the API. No matter what domain is trying to get access to, just accept it and carry on.
AllowAnyMethod means that it doesn’t matter if the request method is GET, POST, PUT, DELETE, or one of those others we never use.
The AllowAnyHeader means that all headers are allowed. Examples of headers are Content-Type, Accept, User-Agent, a custom header, and more. In my example, I don’t care about the headers.
The last one is AllowAnyOrigin. The origin is the request URL, like localhost:4200. The API runs on localhost:7050, which is a different port. Another example could be that https://kenslearningcurve.com requests data from the API. The URL kenslearningcurve.com is the origin URL. In my example, I don’t care what origin tries to access my API.
If you start the API now and refresh the Angular application, you will see movies.
Yes, this works but I would never use this. Reason: The API allows everything which is a security risk. This is a perfect way to use on your local development machine, but not in a test, accept, or production environment.
A better fix: WithOrigins
Better is to be specific about what you want to allow access to. If you are creating an API for specific online applications you could add the IP address or the URLs to the origins. You could also add a specific, custom header so only requests with that specific header are allowed.
If I would change the UseCors to the following, you would get a CORS error in the Angular application.
The reason for the error is that the Angular application isn’t running on port 4201 but on 4200. If you change the port to 4200 everything works fine.
If you have multiple localhost applications or URLs with different ports, you might not want to add all the different localhost-URLs. To fix this, you can use the SetIsOriginAllowed method. It is used to evaluate the origin and determine if the origin is allowed or not. An example:
It checks if the host of the origin is equal to localhost. If so, then the origin is allowed. In this example, it doesn’t matter which port is used, or subdomain.
A better fix: WithHeaders
Another better fix, combined with the previous one, would be to check the header. To make this work, I add a little bit more code to the Angular application:
I have added the header Content-Type to the request. The data doesn’t matter. I stored this header in the HttpHeaders, which I placed in the options of the GET of the HTTPClient.
When I start up the application the request will be sent to /api/movies, along with the header Content-Type.
Let’s go back to the C# API. I change the UseCors to the following:
I removed AllowAnyHeader and added WithHeaders. I can add different, known header names to the parameters. In this case, I have added HeaderNames.ContentType to it. This means that requests with the header Content-Type and with a host of localhost are allowed. If you start the application you will see it works as it should.
You don’t need to use predefined header names. You can also create your own. Let’s change the header in the Angular application to X-OwnCustomHeader:
If you start the application again, along with the API, you will get a CORS error. The API only accepts requests from localhost and with the presence of the header Content-Type.
To allow the new header, simply add it in the API.
Conclusion
Although CORS seems to be a menace when you are developing on your local machine, it is really important for online applications. Especially for APIs. It isn’t really hard to configure if you know how. With a few simple statements you can set the CORS in the API as you want.
Always think about how you want to use the API. Do you want everyone to access it? Do you want to set a specific origin or header? Whatever you do, never allow everything. It can be a security risk.
