11

I'm developing an app on my local pc. THe frontend should be built with spinejs and the backend-api with node.js. Spine is running on port 9294 and node.js is running on port 3000. in Spine I've added to my model the following:

@url: "http:localhost:3000/posts"

and in my express server

app.get('/posts', function(req, res){
  console.log("giving ALL the posts");
  res.header("Access-Control-Allow-Origin", "*")
  res.json(posts);
});

But I'm always getting the following erro in chrome:

XMLHttpRequest cannot load http://localhost:3000/posts. Origin http://localhost:9294 is not allowed by Access-Control-Allow-Origin.

What must I do that I can access my api properly? I though adding the header in the responses does fix the problem.

soupdiver
  • 3,504
  • 9
  • 40
  • 68
  • 1
    Have you inspected the response header to confirm that the header is indeed being set? Also, jQuery uses the 'OPTIONS' HTTP command to detect pre-auth; make sure express is responding to that HTTP Verb as well – tkone Apr 09 '12 at 20:03
  • honestly it seems my request never reaches my node.js server. I've added a debug statement in app.get() BEFORE anything is sent to the client. When accessing my api directly over the url I can see the statement but not when accessing via spine – soupdiver Apr 09 '12 at 20:06
  • YOu should use something like charles to make sure this is happening. We do a ton of x-domain stuff here and it's frustrating until you see that the header ISN'T being set. – tkone Apr 09 '12 at 20:14

2 Answers2

16

app.get will only respond to GET requests. If the browser is preflighting it with an OPTIONS request, express will send an error because it doesn't have any listeners for those requests. Try adding this code in addition to yours and see if it works:

app.options('/posts', function(req, res){
  console.log("writing headers only");
  res.header("Access-Control-Allow-Origin", "*");
  res.end('');
});

Also note: if you're sending cookies with the request (withcredentials=true), then the Access-Control-Allow-Origin header cannot be *, it must be the exact value in the Origin header that the browser automatically adds to the ajax request like so:

res.header("Access-Control-Allow-Origin", req.headers.origin);

This is for security reasons - if you're doing something that requires cookies, then it is more likely that you will want to actually check that the origin is an allowed website in order to avoid CSRF attacks.

Nathan Friedly
  • 7,837
  • 3
  • 42
  • 59
15

This middleware will allow CORS using Express, the key is detecting the preflight request OPTIONS and returning a response to avoid 404's or duplicate database queries. See resource: http://cuppster.com/2012/04/10/cors-middleware-for-node-js-and-express/

var methodOverride = require('method-override');
app.use(methodOverride());

// ## CORS middleware
// see: http://stackoverflow.com/questions/7067966/how-to-allow-cors-in-express-nodejs
var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');

    // intercept OPTIONS method
    if ('OPTIONS' == req.method) {
      res.send(200);
    }
    else {
      next();
    }
};
app.use(allowCrossDomain);
Troy Watt
  • 868
  • 8
  • 16
  • 2
    methodOverride is no longer bundled with Express as of version 4, so you will need to include "method-override" in your package.json, then instead of `app.use(express.methodOverride())`, use `var methodOverride = require('method-override'); app.use(methodOverride());` – Jonathon Blok Aug 05 '14 at 10:11