Friday, June 20, 2014

Avoiding CORS on localhost testing via Simple Node.js Proxy Server

What Is CORS Anyway?

Before I get deeper into this topic, I just want to tell you a bit about CORS. CORS an abbreviation of "Cross Origin Resource Sharing", and it is described in this Wikipedia Article.  To make a short summary of this issue, basically, what it means is that there is a security mechanism in just about every modern browser that prevent you to perform AJAX requests from a different server than where your original content came from.

For example, a typical tutorial lesson you get from books may require that you have a simple web server running on Node.js (really easy and cool) then a separate database server that's serving up JSON data (for example, via a Deployed service), you would do something like http://localhost:5000 for your web contents and then http://localhost:5500 for JSON data server. That triggers CORS.

Of course, if you ask most experts, they will say, the answer is easy. They will say just enable CORS on the server side. But it is not that simple. This is a blog about answers that experts do not know how to answer after all.

The reason I am writing this article is because I spent a couple of days to explore different ways of resolving this issue when I was writing some local development and testing environment to learn Angular.js

Because of a very specific way Angular.js performs the HTTP Get and Post the JSON data, you will find it difficult to get around CORS issue. There is even an extension in Chrome to allow CORS in Chrome but even with it, the POST part of the equation does not work for me. In my case, even if I (thought) modified the header to do;

Access-Control-Allow-Origin *
It still did not give me the result I wanted. I even read that on Firefox, a * is not even permitted, and that a specific host be specified. Pretty tricky if you want to test various browsers too! (*I even implemented a version of Microsft IIS site with web.config edited to allow CORS with no avail!)

It is also even worse when dealing with a localhost than any other hosts to get around CORS.

The Proxy Based Solution

In my situation, what ended up working the best is to completely avoid CORS altogether and make it look as if the database service is coming from the same server. And as a bonus of using Node.js it is quite easy to implement it.

It is basically the use of proxy-middleware (e.g. npm install proxy-middleware) that solved this. In this case, all of my test implementation files are stored under ./angularjs folder, so for example, http://localhost:5000/app.html will pull the file from ./angularjs/app.html

I then created a Deployd server at port 5500 (i.e., http://loclhost:5500) and I wanted my server to proxy all of these requests to that when an AJAX request is made to http://localhost:5000/db  So this means that when I was originally doing http://localhsot:5500/products to get the Products JSON data, I will request http://localhost:5000/db/products

var connect = require('connect')
    , url = require('url')
    , proxy = require('proxy-middleware')
    ;
var app = connect();
app.use(connect.logger('dev'));
app.use(connect.static('./angularjs'));
var proxyFunc = proxy(url.parse('http://localhost:5500'));
app.use('/db', proxyFunc);
app.listen(5000);

Stupid Side Note:

Actually, when I tried this the first time, it did not work, the POST failed, because I had some leftover modifications like this in my code

      app.config(['$httpProvider', function ($httpProvider) {
            //Reset headers to avoid OPTIONS request (aka preflight)
            $httpProvider.defaults.headers.common = {};
            $httpProvider.defaults.headers.post = {};
            $httpProvider.defaults.headers.put = {};
            $httpProvider.defaults.headers.patch = {};
        }]);

Be sure to back out any attempts you've done to fix the issue.  It should just work with a bare-bone POST call of Angular.js HTTP provider!




No comments: