Skip to main content

Backbone.js and cross domain API

· 4 min read
Matej Jelluš
Tech leader and IT nerd who is constantly trying new things, sharing his experiences and still enjoys writing code in his free time. Currently looking for new challenges and opportunities.

I am playing a little bit with PHP micro framework Slim and javascript Backbone.js. Therefor I created two separate projects with URLs : mywebapp.dev for front-end ( Backbone.js ) and api.mywebapp.dev for back-end ( Slim ). The problem occurred nearly immediately : Cross-Origin Resource Sharing ( CORS ).

Backbone.js and CORS

When you create your first model, define the route and try to load it via console, the browser will object and throw exception : No 'Access-Control-Allow-Origin' header is present on the requested resource in Chrome.

When you start to google it out every time you end up reading about CORS problem. So what it is, why is it a problem and how to solve it?

What is Cross-Site Sharing and why does it exist

Good example can be found on Wikipedia page Same-origin policy :

The main reason to have this restriction is because without the same-origin policy there would be a security risk. Assume that a user is visiting a banking website and doesn’t log out. Then he goes to any random other site and that site has some malicious JavaScript code running in the background that requests data from the banking site. Because the user is still logged in on the banking site, that malicious code could do anything on the banking site. For example get a list of your last transactions, create a new transaction, etc.

Solution

To solve Allow-Origin problem you need to add new header to the response. This can be done in many ways :

// pure php
header("Access-Control-Allow-Origin: *");

// slim 3
$response->withHeader('Access-Control-Allow-Origin', '*');

// .htaccess
Header add Access-Control-Allow-Origin "*"

Now if you try to load any model it should work. The problem is, when you try to modify it, or create a new one. Or, to be more precise, there are two problems. First, Bakcbone send data to server in JSON format and that violates Content Security Policy. In Chrome console you see this error : Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

// pure php
header("Access-Control-Allow-Headers: Content-Type");

// slim 3
$response->withHeader('Access-Control-Allow-Headers', 'Content-Type');

// .htaccess
Header add Access-Control-Allow-Headers "Content-Type"

And the second problem is that actions like this invoke HTTP PUT methods call and the browser will stand against them again. Problem is, this method is not considered as safe. Therefore, another header is required :

// pure php
header("Access-Control-Allow-Methods: GET,POST,OPTIONS,DELETE,PUT");

// slim 3
$response->withHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS,DELETE,PUT');

// .htaccess
Header add Access-Control-Allow-Methods "GET,POST,OPTIONS,DELETE,PUT"

Otherwise you get back only error message Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

.htaccess

his is final .htaccess file for Slim 3 framework and CORS allowed :

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Methods "GET,POST,OPTIONS,DELETE,PUT"
Header add Access-Control-Allow-Headers "Content-Type"

Still a problem ( 405 method not allowed )

If you are still getting some error, for example 405 method not allowed, this can be caused by calling the OPTIONS method first. The browser cannot know if it can call POST ( or GET / other methods ), so it does a preflight request that checks if it is allowed to make that type of request. But you do not have this method implemented, so the framework returns an error and therefore the browser will not make the call – even if the headers are set correctly!

var express = require('express');
var app = express();
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.header("Access-Control-Allow-Headers", "Content-Type");
if ( req.method == 'OPTIONS' ) {
res.sendStatus(200);
} else {
next();
}
});

Conclusion

Now you can load and save your models as you wish. If you want to know more, read the links below. And of course when I am done with my little app, I will share the experiences and write another post about it.

References

CORS

Cross-origin resource sharing page on Wikipedia. You find there useful information with an example and I recommend you to read some of the linked pages also.

Same-origin policy page on Wikipedia, where you can navigate from CORS page. It is good to at least read the post to get some overview.

Content Security Policy page on Wikipedia.


Do you like this post? Is it helpful? I am always learning and trying new technologies, processes and approaches. When I struggle with something and finally manage to solve it, I share my experience. If you want to support me, please use button below. If you have any questions or comments, please reach me via email juffalow@juffalow.com.

I am also available as a mentor if you need help with your architecture, engineering team or if you are looking for an experienced person to validate your thoughts.