一個開箱即用,功能完善的 Express 項目

前言

node.js 對前端來講無疑具備里程碑意義,在其愈來愈流行的今天,掌握 node.js 已經不只僅是加分項,而是前端攻城師們必需要掌握的技能。而 express 以其快速、開放、極簡的特性, 成爲 node.js 最流行的框架,因此使用 express 進行 web 服務端的開發是個不錯且可信賴的選擇。可是 express 初始化後,並不立刻就是一個開箱即用,各類功能完善的 web 服務端項目,例如:日誌記錄、錯誤捕獲、mysql 鏈接、token 認證、websocket 等一系列常見的功能,須要開發者本身去安裝插件進行配置完善功能,若是你對 web 服務端開發或者 express 框架不熟悉,那將是一項耗費巨大資源的工做。本文在 express 的初始架構上配置了日誌記錄、錯誤捕獲、mysql 鏈接、token 認證、websocket 等一系列常見的功能,而且本文的項目已經在 github 開源,提供給你們學習、實際項目使用,但願能減輕你們的工做量,更高效完成工做,有更多時間提高本身的能力。javascript

辛苦整理良久,還望手動點贊鼓勵~css

博客 github地址爲:github.com/fengshi123/… ,彙總了做者的全部博客,也歡迎關注及 star ~html

本項目 github 地址爲:github.com/fengshi123/…前端

1、項目結構

1.一、基礎環境

這個 express 服務端項目,相關運行環境配置以下:java

工具名稱 版本號
node.js 11.12.0
npm 6.7.0
express 4.16.0
mysql 5.7

1.二、運行項目

(1)安裝插件,進入到 express_project 目錄,運行如下命令:node

npm install
複製代碼

(2)開發環境啓動,進入到 express_project 目錄,運行如下命令:mysql

npm run dev
複製代碼

(3)正式環境啓動,進入到 express_project 目錄,運行如下命令:git

npm run start
複製代碼

1.三、項目目錄

項目目錄結構以下:github

├─ bin                數據庫初始化腳本				
│  ├─ db	      項目數據庫安裝,其中 setup.sh 爲命令入口,初始化數據庫
├─ common	      共用方法、常量目錄
├─ conf		      數據庫基本配置目錄
├─ dao		      代碼主邏輯目錄
├─ log                日誌目錄					
├─ public	      靜態文件目錄
├─ routes	      url 路由配置						
├─ .eslintrc.js	      eslint 配置
├─ app.js	      express 入口
├─ package.json	      項目依賴包配置
├─ README.md	      項目說明

複製代碼

2、經常使用功能介紹

2.一、結合 mysql

2.1.一、安裝配置 mysql

這裏就不介紹 mysql 的安裝配置,具體操做能夠參照 配置文檔,建議安裝一個 mysql 數據庫管理工具 navicat for mysql ,平時用來查看數據庫數據增刪改查的狀況;web

2.1.二、數據庫初始化

咱們在 bin/db 目錄底下,已經配置編寫好了數據庫初始化腳本,其中 setup.sh 爲編寫好的命令入口,用於鏈接數據庫、新建數據庫等,主要邏輯已經進行註釋以下:

mysql -uroot -p123456 --default-character-set=utf8 <<EOF // 須要將賬號密碼改爲本身的
drop database if exists research; // 刪除數據庫
create database research character set utf8; // 新建數據庫
use research; // 切換到 research 數據庫,對應改爲本身的
source init.sql; // 初始化 sql 建表 
EOF
cmd /k
複製代碼

咱們能夠在 init.sql 中插入每張表的初始數據,相關代碼以下,至於表數據如何插入等簡單邏輯這裏就再也不介紹,讀者能夠查看相關 sql 文件即會了解。

source t_user.sql;
source t_exam.sql;
source t_video.sql;
source t_app_version.sql;
source t_system.sql;
複製代碼

2.1.三、數據庫配置

咱們在 conf/db.js 文件中配置 mysql 的基本信息,相關配置項以下,能夠對應更改配置項,改爲你本身的配置。

module.exports = {
  mysql: {
    host: '127.0.0.1',
    user: 'root',
    password: '123456',
    database: 'research',
    port: 3306
  }
};
複製代碼

2.1.四、數據庫鏈接及操做

對數據進行基本的增、刪、改、查操做,能夠經過已經配置好的文件以及邏輯進行照搬,咱們以增長一個用戶舉例進行簡單介紹,在 dao/userDao.js 文件中:

var conf = require('../conf/db'); // 引入數據庫配置
var pool = mysql.createPool(conf.mysql); // 使用鏈接池

  add: function (req, res, next) {
    pool.getConnection(function (err, connection) {
      if (err) {
        logger.error(err);
        return;
      }
      var param = req.body;
      // 創建鏈接,向表中插入值
      connection.query(sql.insert, [param.uid, param.name, param.password, param.role,param.sex], function (err, result) {
        if (err) {
          logger.error(err);
        } else {
          result = {
            code: 0,
            msg: '增長成功'
          };
        }
        // 以json形式,把操做結果返回給前臺頁面
        common.jsonWrite(res, result);
        // 釋放鏈接
        connection.release();
      });
    });
  },
複製代碼

2.二、日誌功能

2.2.一、morgan 記錄請求日誌

morgan 是 express 默認的日誌中間件,也能夠脫離 express,做爲 node.js 的日誌組件單獨使用,morgan 的具體 api 可參考 morgan 的 github 庫;這裏主要介紹咱們項目中,幫你進行了哪些配置、實現了哪些功能。項目在 app.js 文件中進行了如下配置:

const logger = require('morgan');
// 輸出日誌到目錄
var accessLogStream = fs.createWriteStream(path.join(__dirname, '/log/request.log'), { flags: 'a', encoding: 'utf8' }); 
app.use(logger('combined', { stream: accessLogStream }));
複製代碼

咱們已經配置好以上請求日誌記錄,這樣每次 http 請求都會記錄到 log/request.log 文件中。

2.2.二、winston 記錄錯誤日誌

因爲 morgan 只能記錄 http 請求的日誌,因此咱們還須要 winston 來記錄其它想記錄的日誌,例如:訪問數據庫出錯等;Winston 是 node.js 上最流行的日誌庫之一。它被設計爲一個簡單通用的日誌庫,支持多種傳輸(一種傳輸實際上就是一種存儲設備,例如日誌存儲在哪裏)。winston 中的每個 logger 實例在不一樣的日誌級別能夠存在多個傳輸配置;固然它也能夠記錄請求記錄。winston 的具體 api 可參考 winston 的 github 庫 ,咱們這裏不作詳細的介紹。

這裏主要介紹咱們項目中,咱們使用 winston 幫你進行了哪些配置、實現了哪些功能。項目在 common/logger.js 文件中進行了如下配置:

var { createLogger, format, transports } = require('winston');
var { combine, timestamp, printf } = format;
var path = require('path');

var myFormat = printf(({ level, message, label, timestamp }) => {
  return `${timestamp} ${level}: ${message}`;
});

var logger = createLogger({
  level: 'error',
  format: combine(
    timestamp(),
    myFormat
  ),
  transports: [
    new (transports.Console)(),
    new (transports.File)({
      filename: path.join(__dirname, '../log/error.log')
    })
  ]
});

module.exports = logger;
複製代碼

經過以上 logger.js 文件配置,咱們只須要在須要的地方引入該文件,而後調用 logger.error(err) 進行相應等級的日誌記錄,相關的錯誤日誌就會記錄到文件 log/error.log 中。

2.三、請求路由處理

咱們在項目中按模塊劃分請求處理,咱們在 routes 目錄下分別新建每一個模塊路由配置,例如用戶模塊,則爲 user.js 文件,咱們在 app.js 主入口文件中引入每一個模塊的路由,以用戶模塊進行舉例,相關代碼邏輯以下:

// 引入用戶模塊路由配置
var usersRouter = require('./routes/users');
// 使用用戶模塊的路由配置
app.use('/users', usersRouter);
複製代碼

其中 routes/users.js 文件中的路由配置以下:

var express = require('express');
var router = express.Router();

// 增長用戶
router.post('/addUser', function (req, res, next) {
  userDao.add(req, res, next);
});
// 獲取所有用戶
router.get('/queryAll', function (req, res, next) {
  userDao.queryAll(req, res, next);
});
// 刪除用戶
router.post('/deleteUser', function (req, res, next) {
  userDao.delete(req, res, next);
});
...
複製代碼

經過以上配置,這樣客戶端經過 /users 開頭的請求路徑,都會跳轉到用戶模塊路由中進行處理,假設請求路徑爲 /users/addUser 則會進行增長用戶的邏輯處理。經過這種規範和配置,這時若是增長一個新的模塊,只需按照原有的規範,新增一個模塊路由文件,進行相關的路由配置處理,不會影響到原有的模塊路由配置,也不會出現路由衝突等。

2.四、token 認證

express-jwt 是 node.js 的一箇中間件,他來驗證指定 http 請求的 JsonWebTokens 的有效性,若是有效就將jsonWebTokens 的值設置到 req.user 裏面,而後路由到相應的 router。 此模塊容許您使用 node.js 應用程序中的 JWT 令牌來驗證HTTP請求。express-jwt 的具體 api 可參考 express-jwt 的 github 庫 ,咱們這裏不作詳細的介紹。

這裏主要介紹咱們項目中,咱們使用express-jwt 幫你進行了哪些配置、實現了哪些功能。項目在 app.js 文件中進行了如下 token 攔截配置,若是沒有通過 token 認證的,會返回客戶端 401 認證失敗;

var expressJWT = require('express-jwt');
// token 設置
app.use(expressJWT({
  secret: CONSTANT.SECRET_KEY
}).unless({
  // 除了如下這些 URL,其餘的URL都須要驗證
  path: ['/getToken',
    '/getToken/adminLogin',
    '/appVersion/upload',
    '/appVersion/download',
    /^\/public\/.*/,
    /^\/static\/.*/,
    /^\/user_disk\/.*/,
    /^\/user_video\/.*/
  ]
}));
複製代碼

咱們能夠選擇在生成 token 時,將對應登陸的用戶 id 注入 token 中,如文件 dao/tokenDao.js 文件中所配置的;

// uid 注入 token 中
ret = {
  code: 0,
  data: {
	token: jsonWebToken.sign({
	  uid: obj.uid
	}, CONSTANT.SECRET_KEY, {
	  expiresIn: 60 * 60 * 24
	}),
  }
};
複製代碼

後續咱們能夠在請求中,經過請求信息 req.body.uid,獲取獲得先前注入 token 裏面的用戶 id 信息。

2.五、跨域配置

咱們已經在項目的 app.js 中進行項目的跨域配置,這樣一些客戶端例如:單頁面應用開發、移動應用等, 就能夠跨域訪問服務端對應的接口。咱們在 app.js 文件中進行相關跨域配置:

app.all('*', function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', '*');
  res.header('Access-Control-Allow-Methods', '*');
  next();
});
複製代碼

2.六、靜態目錄配置

咱們在項目中配置了靜態目錄,用於提供靜態資源文件(圖片、css 文件、js 文件等)的服務;傳遞一個包含靜態資源的目錄給 express.static 中間件用於提供靜態資源。以下提供 public 目錄下的圖片、css 文件和 javascript 文件,固然你也能夠根據本身的須要,經過相似配置,建立本身靜態資源目錄。

app.use('/', express.static(path.join(__dirname, 'public')));
複製代碼

2.七、異常錯誤處理

若是咱們若是沒有對服務端程序進行處理,當服務端代碼拋出異常時,會致使 node 進程退出,從而用戶沒法正常訪問服務器,形成嚴重問題。咱們在項目中配置使用 domain 模塊,捕獲 服務端程序中中拋出的異常;domain 主要的 API 有 domain.run 和 error 事件。簡單的說,經過 domain.run 執行的函數中引起的異常均可以經過 domain 的 error 事件捕獲。咱們在 項目中配置使用 domain 的代碼以下:

var domain = require('domain');
// 處理沒有捕獲的異常,致使 node 退出
app.use(function (req, res, next) {  
  var reqDomain = domain.create();  
  reqDomain.on('error', function (err) {   
     res.status(err.status || 500);    
     res.render('error'); 
  }); 
  reqDomain.run(next);
});
複製代碼

2.八、自動重啓

每次修改 js 文件,咱們都須要重啓服務器,這樣修改的內容纔會生效,可是每次重啓比較麻煩,影響開發效果;因此咱們在開發環境中引入 nodemon 插件,實現實時熱更新,自動重啓項目。因此如 1.3 章節所述,咱們在開發環境中啓動項目應該使用 npm run dev 命令,由於咱們在 package.json 文件中配置瞭如下命令:

"scripts": {
    "dev": "nodemon ./app",
  },
複製代碼

2.九、pm2 使用

pm2 是 node 進程管理工具,能夠利用它來簡化不少 node 應用管理的繁瑣任務,如性能監控、自動重啓、負載均衡等,並且使用很是簡單。 因此咱們可使用 pm2 來啓動咱們的服務端程序。這裏咱們不詳細介紹 pm2,若是還不瞭解的同窗能夠查看這篇文檔

2.十、文件上傳處理

服務端應用程序不可避免要處理文件上傳的操做,咱們在項目中爲你們配置好 multiparty 插件,並提供相關的上傳、重命名等操做,相關代碼邏輯及註釋在 dao/common.js 中。若是對 multiparty 插件還不瞭解的同窗,能夠參考 multiparty 插件的 github 庫 ,咱們這裏不作詳細的介紹。

var upload = function (path, req, res, next) {
  return new Promise(function (resolve, reject) {
    // 解析一個文件上傳
    var form = new multiparty.Form();
    // 設置編輯
    form.encoding = 'utf-8';
    // 設置文件存儲路徑
    form.uploadDir = path;
    // 設置單文件大小限制
    form.maxFilesSize = 2000 * 1024 * 1024;
    var textObj = {};
    var imgObj = {};
    form.parse(req, function (err, fields, files) {
      if (err) {
        console.log(err);
      }
      Object.keys(fields).forEach(function (name) { // 文本
        textObj[name] = fields[name];
      });
      Object.keys(files).forEach(function (name) {
        if (files[name] && files[name][0] && files[name][0].originalFilename) {
          imgObj[name] = files[name];
          var newPath = unescape(path + '/' + files[name][0].originalFilename);
          var num = 1;
          var suffix = newPath.split('.').pop();
          var lastIndex = newPath.lastIndexOf('.');
          var tmpname = newPath.substring(0, lastIndex);
          while (fs.existsSync(newPath)) {
            newPath = tmpname + '_' + num + '.' + suffix;
            num++;
          }
          fs.renameSync(files[name][0].path, newPath);
          imgObj[name][0].path = newPath;
        }
      });
      resolve([imgObj, textObj])
    });
  });
};
複製代碼

2.十一、文件/目錄操做

服務端程序對服務器目錄、文件進行操做,是頻率比較高的操做,相對於 node.js 默認的 fs 文件操做模塊,咱們在項目中配置了 fs-extra 插件來對服務器目錄、文件進行操做,fs-extra模塊是系統fs模塊的擴展,提供了更多便利的 API,並繼承了fs模塊的 API ;例如咱們在項目中使用 mkdir 方法來建立文件目錄、ensureDir 方法來確認目錄是否存在、remove 方法來刪除文件、copy 方法來複制文件等,你能夠在 dao/filesDao.js 文件中看到 fs-extra 豐富的操做方法。若是你還未接觸過 fs-extra 插件,建議你能夠查看它的 github 庫,有相關詳細介紹。

2.十二、配置 eslint 代碼檢查

爲了讓項目的代碼風格保持良好且一致,故咱們在項目中添加 eslint 來檢查 js 代碼規範;你能夠在 .eslintignore 文件中配置哪些文件你不想經過 eslint 進行代碼檢查;你還能夠在 .eslintrc.js 文件中配置大家團隊的代碼風格。

3、總結

本文介紹了做者開源的一個開箱即用,各類功能完善的 web 服務端項目的相關功能,例如:mysql 結合、日誌記錄、錯誤捕獲、token 認證、跨域配置、自動重啓、文件上傳處理、eslint 配置 等一系列常見的功能,但願能經過源碼開源和文章的相關介紹,幫助讀者減輕工做量,更高效完成工做,有更多時間提高本身的能力。

辛苦整理良久,還望手動點贊鼓勵~

博客 github地址爲:github.com/fengshi123/… ,彙總了做者的全部博客,也歡迎關注及 star ~

本項目 github 地址爲:github.com/fengshi123/…

相關文章
相關標籤/搜索