8

Basically, I need to run a Node/Express server with json-server to mock a RESTFUL API for a test Angular app I'm developing. The issue is that Google Chrome is throwing an error indicating that I've run into a CORS issue. I've tried configuring my server in such a way as to address the CORS issue . . .

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

. . . to no avail.

Is there anything more I could do without fiddling with JSONP or altering my Angular code in any way? It seems to work just fine on a Rails server.

Edit: As many have suggested, I've attempted to use the cors module to address the issue, but that doesn't seem to be working, either. Here's my server.js file:

var express = require('express');
var app = express();
var path = require("path");
var jsonServer = require("json-server");
var databaseServer = jsonServer.create();
var cors = require("cors");

app.use(cors());
app.use("/", express.static(__dirname));
  var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Example app listening at http://%s:%s', host, port);
});

var object = {
  "fighters": [
    {
      "id": 1,
      "firstName": "Jose",
      "lastName": "Aldo",
      "nickname": "Scarface",
      "wins": 25,
      "losses": 1,
      "draws": 0,
      "imageUrl": "/images/jose_aldo.png"
    },
    {
      "id": 2,
      "firstName": "Conor",
      "lastName": "McGregor",
      "nickname": "",
      "wins": 17,
      "losses": 2,
      "draws": 0,
      "imageUrl": "/images/conor_mcgregor.png"
    }
  ]
};


databaseServer.use(jsonServer.defaults);
databaseServer.use(jsonServer.router(object));
databaseServer.listen(4000);

And here's my Angular service:

var FighterService = angular.module("FighterService", ["ngResource"]);

FighterService.factory("Fighter", ["$resource", function ($resource) {
  return $resource("localhost:4000/fighters/:fighterId",
    { fighterId : "@id" },
    {
      query: {
        method: "GET",
        params: { fighterId: "@id" },
        isArray: true 
      },
      get: { method: "GET" }
    });
}]);
Michael P.
  • 1,373
  • 3
  • 12
  • 33
  • 1
    What error specifically is Chrome giving you? – colefner Apr 24 '15 at 05:49
  • XMLHttpRequest cannot load localhost:4000/fighters.json. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.b @ angular.js:9866 . . . – Michael P. Apr 24 '15 at 06:11
  • Is your Angular app hosted by the same server? – D-side Apr 24 '15 at 11:19
  • No. I don't know how to set it up so that both the database and the app are served from the same server. I've added my server file above. – Michael P. Apr 24 '15 at 12:21

8 Answers8

9

SOLVED

The clue is in the error:

XMLHttpRequest cannot load localhost:4000/fighters.json. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.b

In your Angular, the line

return $resource("localhost:4000/fighters/:fighterId",

thinks you're referring to a URL with protocol "localhost:".

It should be an absolute HTTP URL:

return $resource("http://localhost:4000/fighters/:fighterId",

With the correct CORS support you already have, that works.

randomsock
  • 955
  • 6
  • 9
3

If you are using Json-Server to serve your api, you can disable Cors.

Have a look at the json-server docs https://github.com/typicode/json-server.

Essentially, json-server middlewares defaults to noCors: false

I think you could change it and set it to noCors: true

It might be something like:

const server = jsonServer.create();
const middlewares = jsonServer.defaults({ noCors: true })

// set default middlewares (logger, static, cors and no-cache)
server.use(middlewares);
Willian
  • 3,011
  • 1
  • 15
  • 38
2

If you want use CORS on Express you need enable cors and configurate

var cors = require('cors');

var whitelist = [
  'http://localhost', // add here the url when you access to your angular app
  'http://localhost:8080',
  'http://otherdomain.com'
];

var corsOptions = {
    credentials: true,
    origin: function(origin, callback) {
        var originIsWhitelisted = whitelist.indexOf(origin) !== -1;
        callback(null, originIsWhitelisted);
    },
    methods: ['GET', 'PUT', 'POST', 'PATCH', 'DELETE'],
    allowedHeaders: 'accept, content-type'
};

app.use(cors(corsOptions));

The whilte list for example I'm running express server on 3000 port and my application can run on the port 8080 and works fine, or my app can run on http://otherdomain.com and works too.

rkmax
  • 17,633
  • 23
  • 91
  • 176
  • I have used `app.use(auth);` and `const auth = require("json-server-auth");`, how do i add custom cors settings headers to allow headers – Ashish Kamble May 15 '20 at 12:14
1

If it's only you that need to do testing, the easiest thing might be to open Chrome with --disable-web-security so that the browser does not require CORS headers to treat data as safe. (h/t @Taran)

If others need to test as well, the easiest thing to do might be to use the cors module.

If you want to roll your own Express middleware to handle it, I did the same some time ago. Here's what I came up with. Replace your above Express middleware with this:

app.use(function (req, res, next) {
    'use strict';

    res.header('Access-Control-Allow-Origin', '*');

    if (req.headers['access-control-request-method']) {
        res.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
    }
    if (req.headers['access-control-request-headers']) {
        res.header('Access-Control-Allow-Headers', 'X-Requested-With');
    }

    res.header('Access-Control-Max-Age', 60 * 60 * 24 * 365);

    if (req.method === 'OPTIONS') {
        res.sendStatus(200);
    } else  {
        next();
    }
});

If you need more context, the code is here.

Trott
  • 66,479
  • 23
  • 173
  • 212
1

looks to me (squinting a bit) that you should be able to serve you angular app via the mock server and solve your issues..

- app.use("/", express.static(__dirname)); 
+ app.use("/angular_app", express.static(__dirname));

then link your angular app directory to /angular_app where your mock server is running.

ln -s angular/app ../mock/angular_app

you should be able to access your angular app at localhost:4000/ (or is that :3000?)

lecstor
  • 5,619
  • 21
  • 27
0

If it's just for testing, you can tell Chome to accept CORS by launching it with the --disable-web-security switch.

Taran
  • 12,822
  • 3
  • 43
  • 47
0

You need to enable cors by adding cors module in your express app. Download its dependency using npm command. And add following lines in your node.js file.

var cors = require('cors');

app.use(cors());

Boom
  • 162
  • 1
  • 13
  • @MichaelP. You have added these lines in your server side node file? – Boom Apr 24 '15 at 12:07
  • please take a look on this. You are trying to open file that's why protocol error is occurring. http://stackoverflow.com/questions/27742070/angularjs-error-cross-origin-requests-are-only-supported-for-protocol-schemes – Boom Apr 24 '15 at 12:23
  • Looking . . . If you wouldn't mind, please stand by. This may be the solution. :-D – Michael P. Apr 24 '15 at 12:25
  • Okay. I'm uncertain as to how this solves the problem. I see that it will give me another server, but I'm not certain how that allows me to bypass the CORS issue. – Michael P. Apr 24 '15 at 12:28
  • Be sure that your fighters.json file is located in your node server project? – Boom Apr 24 '15 at 12:36
  • after adding cors module to your sever, hit get request from your angular project to the path e.g $http.get("http://localhost:4000/fighters.json"). But first make sure that .json file exist in server's directory. – Boom Apr 24 '15 at 12:41
0

Given that you specifically mention angular, you could go around the problem by using a reverse proxy within angular

create a proxy.config.json file in the root of your angular project (same folder as the package.json), then add the reverse proxy rules for each api as shown below:

{
    "/api": {
        "target": "http://localhost:3000",
        "secure": true,
        "logLevel": "debug",
        "changeOrigin": true
    },
    "/api-that-is-also-on-port-8080": {
        "target": "http://localhost:3000",
        "secure": true,
        "logLevel": "debug",
        "changeOrigin": true
    },
    "/another-api": {
        "target": "http://localhost:3001",
        "secure": true,
        "logLevel": "debug",
        "changeOrigin": true
    }
}

The above means that any request you send to localhost:4200/api/foo will be reverse proxied onto http://localhost:3000/api/foo (i.e. these calls will no longer be classified as CORS)

This way, in your angular project you can simply use relative paths e.g.

this.http.get('/api/foo') and your request will be channeled to the mock api

You will need to tell angular about the proxy file ng serve --proxy-config proxy.config.json

Alternatively, add the above command to the package.json scripts

"scripts": {
    "start": "ng serve --proxy-config proxy.config.json"
}

and start up angular using

npm run start

sawe
  • 1,141
  • 14
  • 24