【问题标题】:Global Variable in app.js accessible in routes?app.js 中的全局变量可在路由中访问?
【发布时间】:2012-04-03 15:30:04
【问题描述】:

我如何在app.js 中设置一个变量并让它在所有路由中可用,至少在位于路由中的index.js 文件中。使用 express 框架和node.js

【问题讨论】:

  • app.locals 对象的属性是应用程序中的局部变量。在您的路由文件中,使用 req.app.locals 引用它。

标签: node.js express


【解决方案1】:

使用 express 对象上可用的“set”和“get”方法实际上很容易做到这一点。

示例如下,假设您有一个名为 config 的变量,其中包含您希望在其他地方可用的配置相关内容:

在 app.js 中:

var config = require('./config');

app.configure(function() {
  ...
  app.set('config', config); 
  ...
}

在路由/index.js中

exports.index = function(req, res){
  var config = req.app.get('config');
  // config is now available
  ...
}

【讨论】:

  • 将“应用”设置为全局?是的。但这种方法最能帮助我们避免问题和混乱。
  • 对于后来者:app.configure 在 express 4.x 中被删除
  • 很好,req.app.get('name') 就像一个魅力。此属性包含对使用中间件的 Express 应用程序实例的引用。 expressjs.com/pt-br/api.html#req.app
【解决方案2】:

一个巧妙的方法是使用 Express 本身提供的app.localsHere 是文档。

// In app.js:
app.locals.variable_you_need = 42;

// In index.js
exports.route = function(req, res){
    var variable_i_needed = req.app.locals.variable_you_need;
}

【讨论】:

  • 我使用这种方法来定位http服务并在单元测试中轻松覆盖。
  • 我如何重新设置变量?中间服务器正在运行吗?
  • @MoisesRodan - 如果您只想为请求保留值,您可能会发现 res.locals 更有用
【解决方案3】:

要创建一个全局变量,只需声明它而不使用var 关键字。 (一般来说,这不是最佳实践,但在某些情况下它可能很有用 - 请小心,因为它会使变量在任何地方都可用。)

这是来自visionmedia/screenshot-app的示例

文件app.js

/**
 * Module dependencies.
 */

var express = require('express')
  , stylus = require('stylus')
  , redis = require('redis')
  , http = require('http');

app = express();

//... require() route files

文件routes/main.js

//we can now access 'app' without redeclaring it or passing it in...

/*
 * GET home page.
 */

app.get('/', function(req, res, next){
  res.render('index');
});

//...

【讨论】:

  • 请注意,通常最好明确地传递变量并实例化它们,这样您就不会意外地将应用程序中其他地方的 ref 或您拥有的东西清空。
  • 正如保罗所说,这是一种非常糟糕的形式。我会建议下面提到的注入模式。是的,更多样板代码,因此它看起来并不“漂亮”,但它确保您的代码是模块化的……如果这不是您的最后一个项目,您会喜欢的;)
  • 这取决于情况恕我直言。是的,全局变量通常不是一个好习惯(正如我在回答中所说),当从头开始时,我会说将所有内容组织到单独的模块中。但是,如果您要与现有代码集成,通常还有其他考虑因素 - 这恰好是一种(简单但危险的)解决方案。
  • 有人能解释一下为什么这可以参考 Javascript 的语义吗? app 关联到哪个范围?
  • 我认为这个答案不应该标记为correct one。
【解决方案4】:

要声明一个全局变量,您需要使用global 对象。像 global.yourVariableName。但这不是真正的方法。要在模块之间共享变量,请尝试使用注入样式,例如

someModule.js:

module.exports = function(injectedVariable) {
    return {
        somePublicMethod: function() {
        },
        anotherPublicMethod: function() {
        },
    };
};

app.js

var someModule = require('./someModule')(someSharedVariable);

或者你可以使用代理对象来做到这一点。喜欢hub

someModule.js:

var hub = require('hub');

module.somePublicMethod = function() {
    // We can use hub.db here
};

module.anotherPublicMethod = function() {
};

app.js

var hub = require('hub');
hub.db = dbConnection;
var someModule = require('./someModule');

【讨论】:

    【解决方案5】:

    最简单的方法是尽早在 app.js 中声明一个全局变量:

    global.mySpecialVariable = "something"
    

    然后在任何路线上你都可以得到它:

    console.log(mySpecialVariable)
    

    【讨论】:

    • 在 Express 中 - 通常在使用 jshint 或 linting 时 - 这会成为一个问题。
    • 这是一种不好的做法,但你可以告诉 linter 该做什么,并且可以告诉它不要对全局产生任何影响。
    【解决方案6】:

    这里解释得很好,简而言之:

    http://www.hacksparrow.com/global-variables-in-node-js.html

    因此,您正在使用一组 Node 模块,可能是像 Express.js 这样的框架,并且突然觉得需要将一些变量设为全局变量。如何在 Node.js 中使变量成为全局变量?

    对此最常见的建议是“声明不带 var 关键字的变量”或“将变量添加到全局对象”或“将变量添加到 GLOBAL 对象”。你用的是哪一种?

    首先,让我们分析一下全局对象。打开终端,启动 Node REPL(提示)。

    > global.name
    undefined
    > global.name = 'El Capitan'
    > global.name
    'El Capitan'
    > GLOBAL.name
    'El Capitan'
    > delete global.name
    true
    > GLOBAL.name
    undefined
    > name = 'El Capitan'
    'El Capitan'
    > global.name
    'El Capitan'
    > GLOBAL.name
    'El Capitan'
    > var name = 'Sparrow'
    undefined
    > global.name
    'Sparrow'
    

    【讨论】:

    • 在 Express 中 - 通常在使用 jshint 或 linting 时 - 这会成为一个问题。
    • 棉绒不会有问题。但是为什么这只是一半的答案呢?剩下的在哪里?
    【解决方案7】:

    这是一个很有帮助的问题,但如果给出实际的代码示例可能会更有用。即使链接的文章实际上也没有显示实现。因此,我谦虚地提交:

    在您的app.js 文件中,文件顶部:

    var express = require('express')
    , http = require('http')
    , path = require('path');
    
    app = express(); //IMPORTANT!  define the global app variable prior to requiring routes!
    
    var routes = require('./routes');
    

    app.js 不会有 any 引用 app.get() 方法。让这些在单独的路由文件中定义。

    routes/index.js:

    require('./main');
    require('./users');
    

    最后,一个实际的路由文件,routes/main.js

    function index (request, response) {
        response.render('index', { title: 'Express' });
    }
    
    app.get('/',index); // <-- define the routes here now, thanks to the global app variable
    

    【讨论】:

      【解决方案8】:

      我的首选方式是使用循环依赖*,节点支持

      • 在 app.js 中将 var app = module.exports = express(); 定义为您的首要任务
      • 现在任何需要的模块都可以var app = require('./app') 访问它


      app.js
      var express   = require('express');
      var app = module.exports = express(); //now app.js can be required to bring app into any file
      
      //some app/middleware, config, setup, etc, including app.use(app.router)
      
      require('./routes'); //module.exports must be defined before this line
      


      路线/index.js
      var app = require('./app');
      
      app.get('/', function(req, res, next) {
        res.render('index');
      });
      
      //require in some other route files...each of which requires app independently
      require('./user');
      require('./blog');
      

      【讨论】:

      • 很棒的提示。我刚刚从 require('./myroutes')(app) 方法转移到这样做,然后用头撞墙 10 分钟,试图弄清楚为什么我的路线都不起作用。不要忘记将 myroutes.js 中的路由规范从功能块中取出。哇!
      【解决方案9】:

      正如其他人已经分享的那样,app.set('config', config) 非常适合这个。我只是想添加一些我在现有答案中没有看到的非常重要的东西。 Node.js 实例在所有请求之间共享,因此虽然全局共享一些 configrouter 对象可能非常实用,全局存储运行时数据将跨请求和用户使用。考虑这个非常简单的例子:

      var express = require('express');
      var app = express();
      
      app.get('/foo', function(req, res) {
          app.set('message', "Welcome to foo!");
          res.send(app.get('message'));
      });
      
      app.get('/bar', function(req, res) {
          app.set('message', "Welcome to bar!");
      
          // some long running async function
          var foo = function() {
              res.send(app.get('message'));
          };
          setTimeout(foo, 1000);
      });
      
      app.listen(3000);
      

      如果您访问/bar 并且另一个请求点击/foo,您的消息将是“欢迎来到foo!”。这是一个愚蠢的例子,但它抓住了重点。

      Why do different node.js sessions share variables? 有一些有趣的观点。

      【讨论】:

        【解决方案10】:

        这很容易,但人们的答案同时令人困惑和复杂。

        让我向您展示如何在您的 express 应用程序中设置全局变量。因此,您可以根据需要从任何路线访问它。

        假设你想从你的主路由 / 设置一个全局变量

        router.get('/', (req, res, next) => {
        
          req.app.locals.somethingNew = "Hi setting new global var";
        });
        

        因此,您将从所有路线中获得 req.app。然后你必须使用locals 来设置全局数据。像上面那样显示你已经准备好了。现在 我将向您展示如何使用这些数据

        router.get('/register', (req, res, next) => {
        
          console.log(req.app.locals.somethingNew);
        });
        

        像上面从register 访问数据的路由已经设置好了。

        这就是你可以让这个东西工作的方法!

        【讨论】:

          【解决方案11】:

          我用app.all

          app.all() 方法对于映射特定的“全局”逻辑很有用 路径前缀或任意匹配。

          就我而言,我使用confit 进行配置管理,

          app.all('*', function (req, res, next) {
              confit(basedir).create(function (err, config) {
                  if (err) {
                      throw new Error('Failed to load configuration ', err);
                  }
                  app.set('config', config);
                  next();
              });
          });
          

          在路由中,您只需执行req.app.get('config').get('cookie');

          【讨论】:

            【解决方案12】:

            我解决了同样的问题,但我不得不编写更多代码。 我创建了一个server.js 文件,它使用 express 来注册路由。 它公开了一个函数register,其他模块可以使用该函数来注册自己的路由。 它还公开了一个函数 startServer 来开始监听端口

            server.js
            
            const express = require('express');
            const app = express();
            
            const register = (path,method,callback) => methodCalled(path, method, callback)
            
            const methodCalled = (path, method, cb) => {
              switch (method) {
                case 'get':
                  app.get(path, (req, res) => cb(req, res))
                  break;
                ...
                ...  
                default:
                  console.log("there has been an error");
              }
            }
            
            const startServer = (port) => app.listen(port, () => {console.log(`successfully started at ${port}`)})
            
            module.exports = {
              register,
              startServer
            }
            

            在另一个模块中,使用此文件创建路由。

            help.js
            
            const app = require('../server');
            
            const registerHelp = () => {
              app.register('/help','get',(req, res) => {
                res.send("This is the help section")
              }),
              app.register('/help','post',(req, res) => {
                res.send("This is the help section")
              })}
            
            module.exports = {
              registerHelp
            }
            

            在主文件中,引导两者。

            app.js
            
            require('./server').startServer(7000)
            require('./web/help').registerHelp()
            

            【讨论】:

              【解决方案13】:

              John Gordon 的答案是我在许多网站上尝试过的几十个半解释/记录的答案中的第一个,这些答案确实有效。谢谢戈登先生。抱歉,我没有足够的分数来支持你的答案。

              我想补充一点,对于 node-route-file-splitting 的其他新手来说,使用匿名函数来实现 'index' 是人们经常看到的,所以使用 John 的 main.js 示例,通常会找到的功能等效代码是:

              app.get('/',(req, res) {
                  res.render('index', { title: 'Express' });
              });
              

              【讨论】:

                猜你喜欢
                • 2022-08-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-06-09
                • 2017-06-15
                • 2014-11-11
                相关资源
                最近更新 更多