在项目开发中,我们希望有多个环境配置,如开发环境、生产环境、测试环境等。不同的环境可能需要不同的配置,如数据库、日志、端口等。此外,不同的开发者也有不同的设置。
经过前面的实战,我们已经有了下面的目录结构:
koa-blog
├── app
│ ├── middleware
│ │ └── logger.js
│ ├── router
│ │ ├── home.js
│ │ └── index.js
│ ├── util
│ │ └── log_format.js
│ └── view
│ ├── 404.html
│ └── index.html
├── app.js
├── config
│ └── config.js
└── package.json
为了让项目支持不同的开发环境配置,我们将使用以下两个包:
- config - 用来管理不同的运行环境
- dotenv-safe - 用来定义一些需要保密的环境变量。
安装
$ npm install config dotenv-safe --save
配置运行环境
config 会默认去查看项目根目录的 config
文件夹,所以需要创建一个 config
目录,这个在之前的实战已经做了。
接着,来创建一个默认的配置文件 default.json
,其中包含了我们的数据库设置以及服务的启动设置。以本项目为例,配置如下:
// config/default.json
{
"App": {
"ip": "0.0.0.0", // 所有ip可以访问
"port": 3000 // 端口
},
"Router": {
"apiPrefix": "/api" // 路由前缀
},
"Database": {
"user": "moyufed", // MongoDB用户名
"password": "123456", // MongoDB密码
"host": "127.0.0.1",
"dbName": "koaBlog", // MongoDB数据库名
"port": 3001
},
"Log4js": {
"appenders": {
"error": {
"category": "errorLogger", //logger名称
"type": "dateFile", //日志类型
"filename": "logs/error/error", //日志输出位置
"alwaysIncludePattern": true, //是否总是有后缀名
"pattern": "yyyy-MM-dd-hh.log" //后缀,每小时创建一个新的日志文件
},
"response": {
"category": "resLogger",
"type": "dateFile",
"filename": "logs/response/response",
"alwaysIncludePattern": true,
"pattern": "yyyy-MM-dd-hh.log"
}
},
"categories": {
"error": {
"appenders": [
"error"
],
"level": "error"
},
"response": {
"appenders": [
"response"
],
"level": "info"
},
"default": {
"appenders": [
"response"
],
"level": "info"
}
}
}
}
代码里面的配置包括了 config/config.js
里面的所有配置信息。
使用运行环境
在前面的代码中配置了应用的设置 App
以及数据库连接配置 Database
,在项目的任何地方需要使用这些配置时,只需要引用 config
就可以了,如:
// app.js
const Koa = require('koa');
const config = require('config'); // 引入config
const appConfig = config.get('App'); // 直接使用 config 获取App的配置
const apiPrefix = config.get('Router.apiPrefix'); // 可以通过Router.apiPrefix获取具体的值
console.log(appConfig); // 输出获取的 appConfig
// ...
app.listen(appConfig.port, appConfig.ip, () => {
console.log(`服务已经启动,访问:http://localhost:${appConfig.port}${apiPrefix}`);
});
在 router
里面也可以使用config:
// app/router/index.js
const Router = require('koa-router');
const config = require('config'); // 引入config
const apiPrefix = config.get('Router.apiPrefix');
const router = new Router();
router.prefix(apiPrefix); // 设置路由前缀
const home = require('./home');
const index = async (ctx, next) => {
await ctx.render('index', {title: 'Index', link: 'home'});
};
router.get('/', index);
router.get('/index', index);
router.use('/home', home.routes(), home.allowedMethods()); // 设置home的路由
module.exports = router;
当然,我们可以移除之前创建的 config/config.js
文件,接着来对 log
配置部分进行完善:
// app/util/log_format.js
const log4js = require('log4js');
+ const config = require('config'); // 引入config
+ const log4jsConfig= config.get('Log4js');
+ log4js.configure(log4jsConfig);
- const { LOG_CONFIG } = require('../../config/config'); //加载配置文件
- log4js.configure(LOG_CONFIG);
let logFormat = {};
let errorLogger = log4js.getLogger('error'); // categories的元素
let resLogger = log4js.getLogger('response');
// ...
module.exports = logFormat;
启动服务之后,我们就能看到命令行能够打印出 config.json
里面的 App 配置信息:
{ server: '0.0.0.0', port: 3000 }
服务已经启动,访问:http://localhost:3000/api
配置多个环境
经过上面的介绍,我们已经通过 config 来配置运行环境了,但仅是这样并不能实现多个环境的配置,我们需要再配置一个新的环境。
接下来,配置一个生产环境(production),需要在 config
目录新建一个 production.json
文件:
// config/production.json
{
"App": {
"port": 8000
}
}
这里并没有配置所有的变量,而是希望一些变量保持和默认配置一样,如服务启动的地址、数据库名称等等。
为了验证配置是否生效,需要切换到 production
环境:
'export NODE_ENV=production' // Linux
'set NODE_ENV=production' // Windows
同样,为了方便,可以将该命令添加到 package.json
里面:
{
"name": "koa-blog",
"scripts": {
"start": "node app.js",
"prod": "set NODE_ENV=production&&npm start"
},
// ...
}
接下来执行命令 npm run prod
启动服务就能够看到输出的环境配置已经改变,端口变成了 8000
。访问 http://localhost:8000/api ,浏览器正常显示页面。
$ npm run prod
> set NODE_ENV=production&&npm start
> node app.js
{ ip: '0.0.0.0', port: 8000 }
服务已经启动,访问:http://localhost:8000/api
事实上,当调用
config.get('App')
时,会从对应环境的json
文件去取值替换default.json
对应的值。若需要支持更多的运行环境,我们只需要新增其它的文件就行,如staging.json
、qa.json
等。
配置环境变量
大家已经注意到,在前面的配置中,数据库密码是写在 config
里面的,为了安全起见,我们希望把密码配置在本地而不是提交到代码库或者仓库。因此,我们需要用到 dotenv-safe 。
dotenv-safe 可以定义私有的变量,这是 node 进程运行时的变量而不是前面配置的环境变量。dotenv-safe 默认会从项目根目录的 .env
文件中加载配置,下面来看看具体操作。
在根目录新建一个 .env
文件,内容如下:
DB_PASSWORD=123456
上面代码把数据库密码抽离了出来,并且我们会在 .gitignore
文件中忽略掉这个文件:
node_modules/
.idea/
logs/
+ .env
这样就不会提交到仓库了。
接下来我们新建一个 .env.example
文件用来提交到代码库,这个文件没有对变量进行赋值,但是能够表明项目使用的配置,注意一来,其他开发者可以根据这里面的内容设置自己的项目环境。如果这个文件里面定义了 .env
没有的值,程序将停止执行。 .env.example
的内容:
DB_PASSWORD=
然后在 app.js
里面优先引入来进行使用:
+ require('dotenv-safe').config(); // 只需要引入一次
const Koa = require('koa');
const config = require('config'); // 引入config
const appConfig = config.get('App'); // 直接使用 config 获取App的配置
const apiPrefix = config.get('Router.apiPrefix'); // 可以通过Router.apiPrefix获取具体的值
+ console.log(process.env.DB_PASSWORD); // 123456
console.log(appConfig); // 输出获取的 appConfig
// ...
启动服务查看输出:
123456
{ ip: '0.0.0.0', port: 8000 }
服务已经启动,访问:http://localhost:8000/api
使用.env环境变量
接下来,我们将使用定义好的变量来替换 config
里面的配置。我们在 config
目录新增一个文件 custom-environment-variables.json
:
{
"Database": {
"password": "DB_PASSWORD"
}
}
这个 json
文件里面我们对数据库的密码进行了定义,当执行 config.get('Database.password')
时, config
将去查询一个叫 “DB_PASSWORD” 的环境变量。如果查询不到就会使用匹配当前 node 环境的 json 文件的值,如果当前 node 环境的值任然没有设置,就会去查询 default.json
里面设置的默认值 。
验证 app.js
验证是否有效:
require('dotenv-safe').config(); // 只需要引入一次
const Koa = require('koa');
const config = require('config'); // 引入config
const appConfig = config.get('App'); // 直接使用 config 获取App的配置
const apiPrefix = config.get('Router.apiPrefix'); // 可以通过Router.apiPrefix获取具体的值
+ const dbConfig = config.get('Database');
+ console.log(dbConfig);
console.log(process.env.DB_PASSWORD); // 123456
console.log(appConfig); // 输出获取的 appConfig
// ...
修改 .env
里面的值来启动服务查看是否生效:
DB_PASSWORD=12345678
结果:
{ user: 'moyufed',
password: '12345678',
host: '127.0.0.1',
dbName: 'koaBlog',
port: 3001 }
12345678
{ ip: '0.0.0.0', port: 8000 }
服务已经启动,访问:http://localhost:8000/api
我们可以看到,数据库的连接密码已经被 .env
修改为 12345678
。通过这种方式,可以将服务器的一些配置抽离到 .env
文件:
// .env
APP_IP=0.0.0.0
APP_PORT=3000
DB_PASSWORD=123456
DB_HOST=127.0.0.1
DB_PORT=3001
DB_USER=moyufed
DB_NAME=koaBlog
// .env.example
APP_IP=
APP_PORT=
DB_PASSWORD=
DB_HOST=
DB_PORT=
DB_USER=
DB_NAME=
// config/custom-environment-variables.json
{
"App": {
"ip": "APP_IP", // 所有ip可以访问
"port": "APP_PORT" // 端口
},
"Database": {
"user": "DB_USER", // MongoDB用户名
"password": "DB_PASSWORD", // MongoDB密码
"host": "DB_HOST",
"dbName": "DB_NAME", // MongoDB数据库名
"port": "DB_PORT"
}
}
移除引入的旧的config设置
参考资料:Maintain Multiple Environment Configurations and Secrets in Node.js Apps
经过本节实战,我们已经完成了项目的环境配置,我们的项目目录如下:
koa-blog
├── .env.example
├── .env
├── .gitignore
├── app
│ ├── middleware
│ │ └── logger.js
│ ├── router
│ │ ├── home.js
│ │ └── index.js
│ ├── util
│ │ └── log_format.js
│ └── view
│ ├── 404.html
│ └── index.html
├── app.js
├── config
│ ├── custom-environment-variables.json
│ ├── default.json
│ └── production.json
├── package.json
└── README.md
下一步,我们来使用 mongoose 操作 MongoDB 插入一条数据,并且使用MVC(Model View Controller)规范进行开发…