How I took down the CORS dragon

CORS policy error as fierce as a dragon

I made a website with ReactJS which monitors an online database real time, and allows deletion of items therein. The data source is from a .NET server that I wrote previously, which provides an API for the ReactJS website to fetch. I thought the world would be as peaceful as the stream of data flowing over the internet and arrive at my browser, until the day when CORS error came up into my console. I thought it is time to rise up against challenges. Warriors grab your keyboard and favorite IDE, and Let’s begin the journey. If you are a desperate problem-solver and your service is down because of that, you can scroll down to the take-away section to get a condensed answer and bookmark this page for future reference or for further understanding of the CORS topic. You will also find much more contents linked with each other with my other technical “how to” blogs.

Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources on a web page to be requested from another domain outside the domain from which the first resource was served.[1]

I have set up a .NET server which fetches data from a database, and provides an API for deleting individual entries. The API is defined as follows:
- http://localhost:44300/data/getdatatable is mapped to DataController and its GetDataTable action, which returns a json for the frontend to render
- http://localhost:44300/data/deleteid is mapped to DataController and its DeleteID action, which deletes the entry in the database matching the provided ID.

The implementation of API is like the following. To be concise, I only excerpted relative code snippets:

On the other hand, I have a fetching function written in Javascript (for the full frontend code I will write a separate article on how I made a ReactJS table with customized features that I like):

And yarn start!

There is nothing appearing in my table. In such cases the first thing to do is to press Ctrl+Shift+c, call out console.

Here comes CORRRRS!

Round One

— adding CORS header to .NET server

The error says “No ‘Access-Control-Allow-Origin’ header is present on the requested resource”.
So accordingly, we need to add the corresponding header to the resource, which means making the server to return a header with Access-Control-Allow-Origin. After I searched online for quite a while and tried out all null solutions, here is what the solution should be:

I added that middle line of code to the controller and it made everything work properly.

PITFALLS

In some references, they recomment adding proxy option to the package.json file in ReactJS repo. I tried that, and it works if the website is a local one, but it failes when the source is remote.

After the fix, the table is displayed nicely:

Round 2

OK. Now let’s test our deletion button!

As expected, it pops up a confimation box, and we hit “OK” to confim the operation. Then the deletion failed. To figure out why, we brough back out best friend, the console:

CORS again!

The first thing we tried is to do exactly what we did last time, but unfortunately is does not work here any more.

As we can see in the .NET code for controllers, the DeleteID function expects a string from the body of request. And here we have added the Access-Control-Allow-Origin header.

A preflight request is a small request that is sent by the browser before the actual request. It contains information like which HTTP method is used, as well as if any custom HTTP headers are present. The preflight gives the server a chance to examine what the actual request will look like before it’s made. The server can then indicate whether the browser should send the actual request, or return an error to the client without sending the request.

Because we have to provide to .NET API some infomationin in the body section specifying what to delete, we triggered the preflight check of server, consequently getting a CORS error.

And plus, the body content did not get into the controller input properly.

In theory, certain types of requests, such as delete and put, need to ask for the server’s permission before making the actual request. The browser asks for permissions by using what is called a preflight request. But clearly the server rejects such request.

The road of attack is like this: It is true that we are performing deletion operation, but since delete method will trigger preflight, and preflight is going to reject the request, we have to avoid the preflight, therefore avoid using delete method. We will use post method and avoid preflight in order to get our data. I show the code here first and will explain later.

Please do correct me if this is not a recommended practice. But this method works out of other practices.

The body of the posted message, instead of putting a string or Json object in there, need needs to be of type FormData. In this way, the contents of form data, stored as key-value pairs, will smoothly flow into the corresponding controller and argument.

And, that’s it! our website is functioning perfectly. The dragon wailed and fell behind a distant mountain. “It may not come back to me”, I wrote in my diary, “but maybe still showing up in front of people to the other side of the mountain”.

Take-aways:

when you get CORS error:

  1. If you want to access data from server without any parameters send to it, you shall make sure you add “Access-Control-Allow-Origin” header to the controller of server and avoid headers or arbitrary data in body section of the post method.
  2. If you want to send some parameters the server, you shall not only add “Access-Control-Allow-Origin” header to the controller, but also wrap your parameters in a FormData object with keys aligned to the input parameter names of the controller.
  3. Setting proxy keyword in package.json file works only if both server and frontend is run locally, which is unlikely to be useful in real cases (think of why do you need a website to access data from server if you are operating on the server?).

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store