如何在node.js上的express.js框架中启用跨域资源共享(CORS)

105

我正在尝试在Node.js中构建一个Web服务器,它将支持跨域脚本编写,并提供来自公共目录的静态文件。我正在使用express.js,但不确定如何允许跨域脚本编写(Access-Control-Allow-Origin: *)。

我看到了这篇帖子,但并没有找到有用的内容。

var express = require('express')
  , app = express.createServer();

app.get('/', function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    next();
});

app.configure(function () {
    app.use(express.methodOverride());
    app.use(express.bodyParser());
    app.use(app.router);
});

app.configure('development', function () {

    app.use(express.static(__dirname + '/public'));
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function () {


    var oneYear = 31557600000;
    //    app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
    app.use(express.static(__dirname + '/public'));
    app.use(express.errorHandler());
});

app.listen(8888);
console.log('express running at http://localhost:%d', 8888);

请注意app.all与app.get之间的区别。 这是一个OPTIONS请求,而不是GET请求。 - Shimon Doodkin
请参考 local-web-server 了解一个简单的 Node 静态 Web 服务器示例,该服务器支持 CORS。 - Lloyd
请访问enable-cors.org/server_apache.html了解更多信息。 - Mostafa
1
"Access-Control-Allow-Origin", "*" 会让你的 API 更容易被钓鱼应用攻击。考虑只允许已知来源。 - Martin Schneider
8个回答

162

请查看enable-cors.org的示例

在您的ExpressJS应用程序上,使用以下方式处理路由:

app.all('/', function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "X-Requested-With");
  next();
 });

app.get('/', function(req, res, next) {
  // Handle the get for this route
});

app.post('/', function(req, res, next) {
 // Handle the post for this route
});
第一个调用(app.all)应该在您的应用程序中的所有其他路由之前调用(或者至少是您希望启用CORS的路由)。
[编辑]
如果您还想让标题出现在静态文件中,请尝试此操作(确保它在调用use(express.static())之前):
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "X-Requested-With");
  next();
});

我用你的代码进行测试,并从public目录中获取了assets的头信息:

var express = require('express')
  , app = express.createServer();

app.configure(function () {
    app.use(express.methodOverride());
    app.use(express.bodyParser());
    app.use(function(req, res, next) {
      res.header("Access-Control-Allow-Origin", "*");
      res.header("Access-Control-Allow-Headers", "X-Requested-With");
      next();
    });
    app.use(app.router);
});

app.configure('development', function () {
    app.use(express.static(__dirname + '/public'));
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function () {
    app.use(express.static(__dirname + '/public'));
    app.use(express.errorHandler());
});

app.listen(8888);
console.log('express running at http://localhost:%d', 8888);

你当然可以把函数打包成一个模块,这样你就可以像这样做:

// cors.js

module.exports = function() {
  return function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    next();
  };
}

// server.js

cors = require('./cors');
app.use(cors());

嘿,谢谢你的回复。 我按照你的建议做了(第一部分),但在请求头中仍然没有看到任何不同。 我已经附上了我的当前代码。 你能解释一下如何将你的解决方案的其余部分整合进去吗? - Guy
1
我很惊讶,因为你在使用express.static之前使用了app.router,但它并没有修改静态文件的标头;无论如何,我已经更新了我的答案,使其可以工作。 - Michelle Tilley
谢谢!我看到你是对的。从服务器获取的资产都带有请求头。我可能没有清楚表达我的真正问题。 我正在尝试使用get命令向外部服务器发出API调用。这就是我遇到错误的地方: XMLHttpRequest无法加载http://www.SOMEURL.com。源http://localhost:8888不被Access-Control-Allow-Origin允许。 - Guy
我可能误解了。你控制SOMEURL.com上的服务器吗? - Michelle Tilley
抱歉,我现在完全理解你的答案了。非常感谢你的帮助 :) - Guy
显示剩余4条评论

61

按照@Michelle Tilley的解决方案,显然一开始对我没起作用。不确定为什么,可能是因为我使用的是Chrome和不同版本的Node。经过一些小调整,现在它对我起作用了。

app.all('*', function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  next();
});

如果有人遇到和我类似的问题,这可能会有所帮助。


请注意app.all和app.get之间的区别,这是OPTIONS请求而不是GET请求。 - Shimon Doodkin
这对我有用(我正在使用Backbone获取对象)。 我正在尝试弄清楚它是否适用于IE 8 ... 看起来应该可以,但我不知道是否需要任何特殊的东西来处理这个“XDomainRequest”... https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS#Browser_compatibility - Adam Loving
一些信息供未来用户参考:我正在将我的域名重定向到一个 Heroku 存储库,这就是为什么我遇到了这个问题的原因。无论如何,第一个答案在本地运行良好,但在我将其推送到 Heroku 后不起作用。然而,这个答案在推送到 Heroku 后起作用了。 - khollenbeck
@KrisHollenbeck 这在我的Heroku上不起作用,你还做了什么吗? - Ben Craig
@BenCraig,不,但是它在第一次尝试后就停止工作了。所以我现在仍然遇到这个问题。 - khollenbeck
显示剩余3条评论

13

试试这个cors npm模块。

var cors = require('cors')

var app = express()
app.use(cors())

该模块提供了许多功能来微调CORS设置,例如域名白名单,为特定的API启用CORS等。


2
我使用这个:
var app = express();

app
.use(function(req, res, next){
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'X-Requested-With');
    next();
})
.options('*', function(req, res, next){
    res.end();
})
;

h.readFiles('controllers').forEach(function(file){
  require('./controllers/' + file)(app);
})
;

app.listen(port);
console.log('server listening on port ' + port);

这段代码假定你的控制器位于controllers目录中。该目录中的每个文件应该像这样:

module.exports = function(app){

    app.get('/', function(req, res, next){
        res.end('hi');
    });

}

1
推荐使用cors express模块。这样可以白名单域名,特定路由允许/限制域名等。

0

如果您想通过“凭据”使用“cookie”,则必须设置Access-Control-Allow-Credentials: true

app.all('*', function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Credentials', true);
  res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  next();
});

0
app.use(function(req, res, next) {
var allowedOrigins = [
  "http://localhost:4200"
];
var origin = req.headers.origin;
console.log(origin)
console.log(allowedOrigins.indexOf(origin) > -1)
// Website you wish to allow to
if (allowedOrigins.indexOf(origin) > -1) {
  res.setHeader("Access-Control-Allow-Origin", origin);
}

// res.setHeader("Access-Control-Allow-Origin", "http://localhost:4200");

// 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,Authorization"
);

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

});

将此代码添加到您的index.js或server.js文件中,并根据您的要求更改允许的来源数组。


-9

我需要采取的一个额外步骤是将我的URL从http://localhost切换到http://127.0.0.0


你指的是什么? - Blunderfest

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接