Can't allow Cross-Origin Request in local Node

2019-04-15 00:04发布

问题:

I've created a local REST API server in nodejs, which is fetching data from local Mongodb database. I've also created a basic web page, which request this data from the server locally. Now, when I try to get data from web page, it gives me following error:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:4000/todos. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

I've searched about on stackoverflow, and found THIS and THIS solutions. I've added the suggested headers in my main app.js file. But still it gives the same error.

Following is my servers app.js file, where I've added these headers.

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');
var todos = require('./routes/todos');

// load mongoose package
var mongoose = require('mongoose');

// Use native Node promises
mongoose.Promise = global.Promise;

// connect to MongoDB
mongoose.connect('mongodb://localhost/todo-api')
.then(() =>  console.log('connection succesful'))
.catch((err) => console.error(err));

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);
app.use('/todos', todos);

// Add headers
 app.use(function (req, res, next) {

    // Website you wish to allow to connect
    res.setHeader('Access-Control-Allow-Origin', '*');

    // Request methods you wish to allow
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT,    PATCH, DELETE');

// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');

// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);

// Pass to next layer of middleware
next();
});
 // catch 404 and forward to error handler
    app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

module.exports = app;

And following is the code(Angularjs) of web page, from where I want to get data from my API.

dbConnection.html

<html ng-app="demo">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"> </script>
<head>
    <title> dbConnection Demo</title>
</head>

<body ng-controller="db">
    <div ng-repeat="product in db.products">
        {{product._id}} </br>
    </div>
</body>

<script>
    var app = angular.module('demo', []);
    app.controller('db', ['$http', function($http){
        var store = this;
        store.products = [];

        $http({
            method: 'GET',
            url: 'http://localhost:4000/todos'
            }).then(function (success){
                store.products = success;
            },function (error){

            });
    }]);
</script>
</html>

Even after I've added headers as suggested in the answers, I'm getting the same error. What am I missing here? I'm completely newbie in this field. Thanks!

回答1:

Does your request require a pre-flight response? If it does, it will be trying to hit your endpoint with method 'OPTIONS' and unless you have set one up, you will see the cors issue.

So if you find that the preflight response is failing, you can either add a custom options route, or potential use the npm cors package - https://www.npmjs.com/package/cors

npm install cors --save

In your app.js

var cors = require('cors')


app.options('*', cors()) // include before other routes 
app.use(cors())


回答2:

I finally figured out the solution by adding those headers in my routes as following:

routes/todos.js

...
...
router.get('/', function(req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); // If needed
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,contenttype'); // If needed
res.setHeader('Access-Control-Allow-Credentials', true); // If needed

res.send('cors problem fixed:)');
});


回答3:

You might have to add:

 res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");

Why? Well I think I had the same problem and this solved it for me.



回答4:

Please move the header code after var app = express(); That mean you must place them before define router.

    var app = express();
 app.use(function (req, res, next) {

// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', '*');

// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT,    PATCH, DELETE');

// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');

// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);

// Pass to next layer of middleware
next();
});

// Router