主题预览

色调:

如果您使用的浏览器过时,不支持OKLCH颜色。颜色设置会不生效。

轻盈的鱼
幻想变成轻盈的鱼, 畅游在自由的海洋

express实践

8176 字

Node.js 7.0发布之前,写一篇关于express的文章

项目创建

使用express-generator

  • 使用npm install express-generator -g

代码修改自动重启

使用nodemon

  • nodemon ./bin/www

全局设置

//加载配置项
global.Config = require(path.resolve(Root, './config/config'));
//通用方法(设置为全局对象,方便调用)
global.F = require(path.resolve(Root, './common/funcs'));
//socket事件方法
global.Socket = require(path.resolve(Root, './socket/socket'));
//加载所有的数据库model
global.M = {};
//model存储路径 (暂时不支持二级目录)
var modelsPath = path.resolve(Root, 'models');
fs.readdirSync(modelsPath).forEach(function (name) {
  if (path.extname(name) !== '') {
    name = path.basename(name, '.js');
    M[name] = require(path.resolve(modelsPath,name));
  }
});

使用co、Promise(bluebird)进行异步处理

定义通用方法run实行Promise,并抛出错误

let run = (fn,next) =>{
  co(fn).catch((err) => {
    next(err);
  });
};

通用方法列表 (大部分抄袭thinkjs)

module.exports = {
    run:run,
    promisify:promisify,
    camelCase:camelCase,
    defer:defer,
    Class:Class,
    extend:extend,
    isClass:isClass,
    isBoolean:isBoolean,
    isNumber:isNumber,
    isObject:isObject,
    isString:isString,
    isArray:isArray,
    isFunction:isFunction,
    isDate: util.isDate,
    isRegExp: util.isRegExp,
    isError: util.isError,
    isIP: net.isIP,
    isIP4: net.isIPv4,
    isIP6: net.isIPv6,
    isFile:isFile,
    isFileAsync:isFileAsync,
    isDir:isDir,
    isDirAsync:isDirAsync,
    isNumberString:isNumberString,
    isPromise:isPromise,
    isWritable:isWritable,
    isBuffer:isBuffer,
    isTrueEmpty:isTrueEmpty,
    isEmpty:isEmpty,
    clone:clone,
    mkdir:mkdir,
    rmdir:rmdir,
    md5:md5,
    chmod:chmod,
    getFiles:getFiles,
    escapeHtml:escapeHtml,
    datetime:datetime,
    getDateTime:getDateTime,
    randomString:randomString
}

数据库操作(mysql)

简单的数据库查询,没有封装sql(懒)

var mysql = require('mysql');
var Promise = require('bluebird');
var dbConfig = Config.db;

var pool  = mysql.createPool(dbConfig);

//使用连接池
let query = sql => {
  return new Promise((resolve,reject) => {
    pool.getConnection((err, connection) => {
      connection.query(sql, function(err, result) {
        return err ? reject(err) : resolve(result);
      });
      connection.release();
    });
  });
};

let insert = (table,data) => {
  return new Promise((resolve,reject) => {
    pool.getConnection((err, connection) => {
      connection.query('INSERT INTO {table} SET ?', data, function(err, result) {
        return err ? reject(err) : resolve(result.insertId);
      });
      connection.release();
    });
  });
};


//普通连接
let queryTest = sql => {
  var connection = mysql.createConnection(dbConfig);
  return new Promise((resolve,reject) => {
    connection.connect();
    connection.query(sql, (err, rows, fields) => {
      return err ? reject(err) : resolve(rows);
    });
    connection.end();
  });
};


module.exports = {
  query:query,
  queryTest:queryTest
};

数据库连接测试代码 (使用了上面定义的run方法)

router.get('/', function(req, res, next) {
  // 数据库操作测试
  F.run(function* (){
       var result = yield Db.query('SELECT * FROM fruitscities limit 5');
       var result = yield Db.query('SELECT * FROM fruitscities limit 5');
       req.session.admin = '111111111';
       console.log(req.session.admin);
       res.render('index', { title: 'Express```'});
  },next)
});

router.get('/test', function(req, res, next) {
  console.log(req.baseUrl);
  console.log(req.route);
  console.log(req.path);
  // 数据库操作测试
  F.run(function* (){
       var result = yield Db.query('SELECT * FROM fruitscities limit 5');
       var result = yield Db.query('SELECT * FROM fruitscities limit 5');
       console.log(req.session.admin);
  },next)
});

使用nunjucks模板引擎

//模板引擎
var nunjucks = require('nunjucks');
// 模板引擎设置
var env = new nunjucks.configure(path.join(__dirname, 'views'), { // 设置模板文件的目录,为views
  autoescape: true,
  watch: true,
  express: app
});
app.set('view engine', 'html');

使用nunjucks模板标签(用于cms,文章,新闻展示等)

//标签测试
var tags = require(path.resolve(Root, './common/tags'));

//绑定标签
env.addExtension("tagtest",new tags.tagtest());
common/tags.js中的代码

var tags;
var nunjucks = require('nunjucks');
tags = {
  tagtest:function(){
    //tag标签测试
    this.tags = ['tagtest'];
    this.parse = function(parser, nodes, lexer) {
      let tok = parser.nextToken();
      var args = parser.parseSignature(null, true);
      parser.advanceAfterBlockEnd(tok.value);
      let body = parser.parseUntilBlocks('endtagtest');// 结束标签
      parser.advanceAfterBlockEnd();
      //return new nodes.CallExtension(this, 'run', args);
      return new nodes.CallExtensionAsync(this, 'run', args,[body]); //异步调用
    };
    this.run = function(context,args,body,callback){
      console.log(JSON.stringify(args)); //前台返回参数
      var data = [{
        id: 1,
        city: '北京',
        parent: 0,
        spelling: 'BeiJing',
        abbr: 'BJ',
        short: 'B' },
        {
          id: 2,
          city: '上海',
          parent: 0,
          spelling: 'ShangHai',
          abbr: 'SH',
          short: 'S' },
        {
          id: 3,
          city: '天津',
          parent: 0,
          spelling: 'TianJin',
          abbr: 'TJ',
          short: 'T' },
        {
          id: 4,
          city: '重庆',
          parent: 0,
          spelling: 'ZhongQing',
          abbr: 'ZQ',
          short: 'Z' },
        {
          id: 5,
          city: '黑龙江',
          parent: 0,
          spelling: 'HeiLongJiang',
          abbr: 'HLJ',
          short: 'H' } ];
      context.ctx['list'] = data;  //返回参数
      let result = new nunjucks.runtime.SafeString(body());
      return callback(null,result);
    }
  }
};

module.exports = tags;

页面中使用

{% tagtest list="key1=1,key2=2,key3=3,key4=4" %}

  {% for val in list %}
  

    {{ val.id }}:{{ val.city}}
  

  {% endfor %}

{% endtagtest %}


{% tagtest key1=1,key2=2,key3=3,key4=4 %}

  {% for val in list %}
  

    {{ val.id }}:{{ val.city}}
  

  {% endfor %}

{% endtagtest %}

使用log4js进行日志记录

记录access日志

app.use(log4js.connectLogger(log4js.getLogger('log_access'), {level:'INFO'}));

配置文件 (配置可以根据需求修改)

module.exports = {
  db: {
    host     : '',
    user     : '',
    password : '',
    database : ''
  },
  cookieSession: {
    name: 'session_uuice',
    keys: ['key1', 'key2'],
    secret: 'ksjf493248kjkj'
  },
  log4js :{
    "appenders":
        [
            {
                "type":"console",
                "category":"console"
            },
            {
                "category":"log_file",
                "type": "file",
                "filename": "./logs/log_file/file.log",
                "maxLogSize": 104800,
                "backups": 100
            },
            {
                "category":"log_date",
                "type": "dateFile",
                "filename": "./logs/log_date/date",
                "alwaysIncludePattern": true,
                "pattern": "-yyyy-MM-dd-hh.log"

            },
            {
                "category":"log_access",
                "type": "dateFile",
                "filename": "./logs/log_access/date",
                "alwaysIncludePattern": true,
                "pattern": "-yyyy-MM-dd-hh.log"

            }
        ],
      "replaceConsole": true,
      "levels":
      {
          "log_file":"ALL",
          "console":"ALL",
          "log_date":"ALL"
      }
  }
};

其他记录日志代码

console.log("log_start start!");

var LogFile = log4js.getLogger('log_file');

LogFile.trace('This is a Log4js-Test');
LogFile.debug('We Write Logs with log4js');
LogFile.info('You can find logs-files in the log-dir');
LogFile.warn('log-dir is a configuration-item in the log4js.json');
LogFile.error('In This Test log-dir is : \'./logs/log_test/\'');

console.log("log_start end!");



var log_date = log4js.getLogger('log_date');

log_date.trace('This is a Log4js-Test');
log_date.debug('We Write Logs with log4js');
log_date.info('You can find logs-files in the log-dir');
log_date.warn('log-dir is a configuration-item in the log4js.json');
log_date.error('In This Test log-dir is : \'./logs/log_test/\'');

console.log("log_date end!");

socket通讯

使用socket.io 在项目bin/www中添加

 //添加socket.io支持
 var io = require('socket.io')(server);
 io.on('connection', function(socket){
   Socket(socket,io); //这里的Socket,就是app.js定义的全局变量
 });

在app.js中添加事件逻辑

//socket事件方法
global.Socket = require(path.resolve(Root, './socket/socket'));

错误处理 Promise中的报错

let run = (fn,next) =>{
  co(fn).catch((err) => {
    next(err);
  });
};

其他报错

// 将404交给错误处理中间件
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// 错误处理

//开发环境报错,显示错误堆栈
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    if(req.xhr){
      res.json({
        status: err.status,
        message: err.message,
        error: err.stack
      });
    }else{
      res.render('error', {
        status: err.status,
        message: err.message,
        error: err
      });
    }

  });
}

//生产环境报错,不展示错误堆栈
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  if(req.xhr){
    res.json({
      status: err.status,
      message: err.message,
      error: {}
    });
  }else{
    res.render('error', {
      status: err.status,
      message: err.message,
      error: {}
    });
  }
});

其他

待补充