最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - I'm attempting to use json-server, Node, and Express to mock a RESTFUL API for my Angular app on localhost.

programmeradmin2浏览0评论

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" }
    });
}]);

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" }
    });
}]);
Share Improve this question edited Apr 24, 2015 at 12:18 Michael P. asked Apr 24, 2015 at 5:45 Michael P.Michael P. 1,4133 gold badges14 silver badges33 bronze badges 4
  • 1 What error specifically is Chrome giving you? – colefner Commented Apr 24, 2015 at 5: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. Commented Apr 24, 2015 at 6:11
  • Is your Angular app hosted by the same server? – D-side Commented Apr 24, 2015 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. Commented Apr 24, 2015 at 12:21
Add a comment  | 

8 Answers 8

Reset to default 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.

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);

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.

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.

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?)

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

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());

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

发布评论

评论列表(0)

  1. 暂无评论