用 node-convict 驯服配置 – Node.JS 假日季,第 7 部分

这是来自 Mozilla 身份团队的Node.JS 假日季系列的第 7 集,共 12 集。今天我们将讨论配置。

在本期“Node.JS 假日季”系列中,我们将介绍node-convict,它是一个帮助管理 node.js 应用程序配置的工具。它提供透明的默认值和内置类型,使错误更容易发现和调试。

问题

关于应用程序配置,有两个主要问题

  • 大多数应用程序至少会有几个不同的部署环境,每个环境都有自己的配置需求。
  • 在源代码中包含凭据和敏感信息可能存在问题。

这些问题可以通过根据环境初始化某些设置,并使用环境变量来存放更敏感的设置来解决。node.js 开发者常用的模式是创建一个导出配置的模块,例如

var conf = {
  // the application environment
  // "production", "development", or "test
  env: process.env.NODE_ENV || "development",

  // the IP address to bind
  ip: process.env.IP_ADDRESS || "127.0.0.1",

  // the port to bind
  port: process.env.PORT || 0,

  // database settings
  database: {
    host: process.env.DB_HOST || "localhost:8091"
  }
};

module.exports = conf;

这已经足够好了,但仍然有一些其他问题

  • 如果某个设置配置错误怎么办?我们可以通过尽早检测和报告错误配置来节省麻烦。
  • 运维/测试/和其他可能需要调整设置或诊断问题的协作者是否能轻松理解它?更具声明性的格式,同时包含文档,可以使生活更轻松。

登场 convict

Convict 通过引入配置模式来解决这些其他问题,您可以在其中为每个设置设置类型信息、默认值、环境变量和文档

使用 convict,上面的示例变为

var convict = require('convict');

var conf = convict({
  env: {
    doc: "The applicaton environment.",
    format: ["production", "development", "test"],
    default: "development",
    env: "NODE_ENV"
  },
  ip: {
    doc: "The IP address to bind.",
    format: "ipaddress",
    default: "127.0.0.1",
    env: "IP_ADDRESS"
  },
  port: {
    doc: "The port to bind.",
    format: "port",
    default: 0,
    env: "PORT"
  },
  database: {
    host: {
      default: "localhost:8091",
      env: "DB_HOST"
    }
  }
});

conf.validate();

module.exports = conf;

信息大体相同,但编码在模式中。由于所有信息都编码在模式中,我们可以导出它并以更易读的格式显示它,并且可以使用它进行验证。这种声明式方法是 convict 更健壮和更易于协作的原因。

模式中包含什么

您会注意到每个设置有四个可能的属性——每个属性都有助于我们实现更健壮和更易于理解的配置。

  • 类型信息format 属性指定内置 convict 格式(ipaddressportint 等),或者可以是一个函数来检查自定义格式。在验证期间,如果格式检查失败,它将被添加到错误报告中。
  • 默认值:每个设置必须具有默认值。
  • 环境变量:如果env指定的变量有值,它将覆盖设置的默认值。
  • 文档doc 属性非常直观。将其放在模式中而不是注释中的好处是,我们可以调用conf.toSchemaString()并将其显示在输出中。

分层添加配置

默认值的集合成为一个基础配置,您可以在其上叠加额外的配置,使用conf.loadconf.loadFile。例如,您可以根据条件叠加一个包含特定环境设置的 JavaScript 对象

var conf = convict({
  // snip ... assume the same schema as above
});

if (conf.get('env') === 'production') {
  // use the production port and database host
  conf.load({
    port: 8080,
    database: {
      host: "ec2-117-21-174-242.compute-1.amazonaws.com:8091"
    }
  });
}

conf.validate();

module.exports = conf;

或者,如果您为每个环境创建单独的配置文件,您可以简单地使用loadFile来叠加它们

conf.loadFile('./config/' + conf.get('env') + '.json');

loadFile还可以通过传入一个数组来一次加载多个文件

// CONFIG_FILES=/path/to/production.json,/path/to/secrets.json,/path/to/sitespecific.json
conf.loadFile(process.env.CONFIG_FILES.split(','));

使用loadloadFile分层配置非常有用,如果您有特定环境的公共设置,这些设置不需要在环境变量中设置。拥有单独的、声明性的 JSON 配置可以提高对哪些设置应该在环境之间改变的可见性。这些文件使用cjson加载,因此它们可以包含任意数量的注释以供进一步说明。

还要注意,环境变量始终优先,即使在使用loadloadFile加载设置之后也是如此。要查看当前设置是什么样,您可以使用conf.toString()将整个内容序列化。

V 代表验证

设置组合好后,您可以执行验证,以检查每个设置的值是否符合模式中定义的正确格式。Convict 提供了一些内置格式,例如"url""ports""ipaddress"等,您也可以使用 JavaScript 的全局构造函数(例如Number)来指定类型。如果您完全省略了format属性,convict 将检查设置是否与默认值具有相同的类型(根据Object.prototype.toString.call)。例如,以下三个模式是等效的

var conf1 = convict({
    name: {
      format: String
      default: 'Brendan'
    }
  });

// with no format specified, convict will assume it's the type
// of the default value
var conf2 = convict({
    name: {
      default: 'Brendan'
    }
  });

// a more succinct version
var conf3 = convict({
    name: 'Brendan'
  });

此外,还有一个枚举式格式,如示例中所示,您可以在其中指定一组明确的允许值,例如["production", "development", "test"]。列表中不存在的任何值都会导致验证失败。

除了内置类型之外,您还可以提供自己的格式检查函数。例如,此模式中的格式函数检查设置是否为 64 个字符的十六进制字符串

var check = require('validator').check;

var conf = convict({
    key: {
      doc: "API key",
      format: function (val) {
        check(val, 'should be a 64 character hex key').regex(/^[a-fA-F0-9]{64}$/);
      },
      default: '3cec609c9bc601c047af917a544645c50caf8cd606806b4e0a23312441014deb'
    }
  });

调用conf.validate()将在任何验证失败的设置上抛出一个错误,其中包含有关每个设置的详细信息。这对避免在每次配置错误后重新部署非常重要。使用自定义键格式示例中的配置,下面是错误的外观

conf.set('key', 'foo');
conf.validate();
// Error: key: should be a 64 character hex key: value was "foo"

总结

Convict 以一种更健壮、更易于协作者访问的方式扩展了配置 node.js 应用程序的标准模式,这些协作者可能不太愿意深入研究命令式代码以检查或修改设置。使用配置模式,我们可以为项目协作者提供有关每个设置的更多上下文,并提供配置错误时的验证和早期错误

系列中的先前文章

这是一个包含 12 篇关于 Node.js 的文章的系列中的第七部分。之前的文章是

关于 Robert Nyman [荣誉编辑]

Mozilla Hacks 的技术布道师和编辑。发表关于 HTML5、JavaScript 和开放网络的演讲和博客文章。Robert 是 HTML5 和开放网络的坚定支持者,自 1999 年以来一直在从事网络前端开发工作——在瑞典和纽约市。他还经常在http://robertnyman.com上发表博客文章,喜欢旅行和结识新朋友。

更多由 Robert Nyman [荣誉编辑] 撰写的文章……


2 条评论

  1. Aaron

    有趣的方法,我用 envalid 创建了一些类似于 convict 的东西:https://github.com/af/envalid/

    绝对同意大多数服务器程序应该对其环境进行验证和清理,而不是在整个代码库中随机访问 process.env。感谢这个系列的文章,其中有很多很好的技巧!

    2013 年 3 月 8 日 08:08

  2. Josh Schell

    Yo,感谢这篇文章。一个月前,我刚遇到这个问题,当时我尝试将我的应用程序推送到 dotcloud 和 github。我自己的处理方法,但今后这对未来的项目来说会更简洁、更容易使用。

    2013 年 3 月 8 日 09:06

本文的评论已关闭。