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/…前端
這個 express 服務端項目,相關運行環境配置以下:java
工具名稱 | 版本號 |
---|---|
node.js | 11.12.0 |
npm | 6.7.0 |
express | 4.16.0 |
mysql | 5.7 |
(1)安裝插件,進入到 express_project 目錄,運行如下命令:node
npm install
複製代碼
(2)開發環境啓動,進入到 express_project 目錄,運行如下命令:mysql
npm run dev
複製代碼
(3)正式環境啓動,進入到 express_project 目錄,運行如下命令:git
npm run start
複製代碼
項目目錄結構以下:github
├─ bin 數據庫初始化腳本
│ ├─ db 項目數據庫安裝,其中 setup.sh 爲命令入口,初始化數據庫
├─ common 共用方法、常量目錄
├─ conf 數據庫基本配置目錄
├─ dao 代碼主邏輯目錄
├─ log 日誌目錄
├─ public 靜態文件目錄
├─ routes url 路由配置
├─ .eslintrc.js eslint 配置
├─ app.js express 入口
├─ package.json 項目依賴包配置
├─ README.md 項目說明
複製代碼
這裏就不介紹 mysql 的安裝配置,具體操做能夠參照 配置文檔,建議安裝一個 mysql 數據庫管理工具 navicat for mysql
,平時用來查看數據庫數據增刪改查的狀況;web
咱們在 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;
複製代碼
咱們在 conf/db.js 文件中配置 mysql 的基本信息,相關配置項以下,能夠對應更改配置項,改爲你本身的配置。
module.exports = {
mysql: {
host: '127.0.0.1',
user: 'root',
password: '123456',
database: 'research',
port: 3306
}
};
複製代碼
對數據進行基本的增、刪、改、查操做,能夠經過已經配置好的文件以及邏輯進行照搬,咱們以增長一個用戶舉例進行簡單介紹,在 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();
});
});
},
複製代碼
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 文件中。
因爲 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 中。
咱們在項目中按模塊劃分請求處理,咱們在 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 則會進行增長用戶的邏輯處理。經過這種規範和配置,這時若是增長一個新的模塊,只需按照原有的規範,新增一個模塊路由文件,進行相關的路由配置處理,不會影響到原有的模塊路由配置,也不會出現路由衝突等。
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 信息。
咱們已經在項目的 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();
});
複製代碼
咱們在項目中配置了靜態目錄,用於提供靜態資源文件(圖片、css 文件、js 文件等)的服務;傳遞一個包含靜態資源的目錄給 express.static 中間件用於提供靜態資源。以下提供 public 目錄下的圖片、css 文件和 javascript 文件,固然你也能夠根據本身的須要,經過相似配置,建立本身靜態資源目錄。
app.use('/', express.static(path.join(__dirname, 'public')));
複製代碼
若是咱們若是沒有對服務端程序進行處理,當服務端代碼拋出異常時,會致使 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);
});
複製代碼
每次修改 js 文件,咱們都須要重啓服務器,這樣修改的內容纔會生效,可是每次重啓比較麻煩,影響開發效果;因此咱們在開發環境中引入 nodemon 插件,實現實時熱更新,自動重啓項目。因此如 1.3 章節所述,咱們在開發環境中啓動項目應該使用 npm run dev 命令,由於咱們在 package.json 文件中配置瞭如下命令:
"scripts": {
"dev": "nodemon ./app",
},
複製代碼
pm2 是 node 進程管理工具,能夠利用它來簡化不少 node 應用管理的繁瑣任務,如性能監控、自動重啓、負載均衡等,並且使用很是簡單。 因此咱們可使用 pm2 來啓動咱們的服務端程序。這裏咱們不詳細介紹 pm2,若是還不瞭解的同窗能夠查看這篇文檔。
服務端應用程序不可避免要處理文件上傳的操做,咱們在項目中爲你們配置好 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])
});
});
};
複製代碼
服務端程序對服務器目錄、文件進行操做,是頻率比較高的操做,相對於 node.js 默認的 fs 文件操做模塊,咱們在項目中配置了 fs-extra 插件來對服務器目錄、文件進行操做,fs-extra模塊是系統fs模塊的擴展,提供了更多便利的 API,並繼承了fs模塊的 API ;例如咱們在項目中使用 mkdir 方法來建立文件目錄、ensureDir 方法來確認目錄是否存在、remove 方法來刪除文件、copy 方法來複制文件等,你能夠在 dao/filesDao.js 文件中看到 fs-extra 豐富的操做方法。若是你還未接觸過 fs-extra 插件,建議你能夠查看它的 github 庫,有相關詳細介紹。
爲了讓項目的代碼風格保持良好且一致,故咱們在項目中添加 eslint 來檢查 js 代碼規範;你能夠在 .eslintignore 文件中配置哪些文件你不想經過 eslint 進行代碼檢查;你還能夠在 .eslintrc.js 文件中配置大家團隊的代碼風格。
本文介紹了做者開源的一個開箱即用,各類功能完善的 web 服務端項目的相關功能,例如:mysql 結合、日誌記錄、錯誤捕獲、token 認證、跨域配置、自動重啓、文件上傳處理、eslint 配置 等一系列常見的功能,但願能經過源碼開源和文章的相關介紹,幫助讀者減輕工做量,更高效完成工做,有更多時間提高本身的能力。
辛苦整理良久,還望手動點贊鼓勵~
博客 github地址爲:github.com/fengshi123/… ,彙總了做者的全部博客,也歡迎關注及 star ~
本項目 github 地址爲:github.com/fengshi123/…