从零开始搭建一个Node的项目,使用Express框架,使用Sqlite作为数据库,废话不多说,直接开始
初始化项目
- 创建空文件,然后init项目
mkdir node-project
cd node-project
npm init
创建项目目录
|—— common/ //公共模块
|—— authenticate.js //认证管理
|—— database.js //数据库连接池
|—— webSocket.js //websocket服务
|—— Interface/ //接口
|—— user.js //用户管理接口
|—— ...
|—— Logs/ //日志
|—— info.log //详细日志文件
|—— error.log //错误日志文件
|—— ...
|—— Routes/ //路由
|—— SQLiteStudio/ //数据库管理工具(忽略该文件夹)
|—— .env //环境变量
|—— .gitignore //忽略文件
|—— index.js //入口文件
|—— package.json //项目配置文件
|—— README.md //项目说明
|—— SQLite.db //数据库文件
|—— SQLite.sql //数据库脚本
|—— swaggerConfig.js //swagger配置文件
安装相关模块
npm install nodemon –save
npm install dotenv –save
npm install express –save
npm install sqlite3 –save
npm install generic-pool –save
npm install log4js –save
npm install jsonwebtoken –save
npm install express-jsdoc-swagger –save
npm install swagger-jsdoc –save
npm install express-ws –save
npm install cors –save
npm install body-parser –save
热更新启动
安装 nodemon 模块,在package.json中添加启动命令。
npm install nodemon –save
// package.json
"scripts": {
"dev": "nodemon index.js"
}
环境变量
安装 dotenv 模块,创建.env 文件,并设置环境变量。
npm install dotenv –save
// .env
PORT = 8888
SECRET_KEY = "123456"
Express
Express 是一个用于 Node.js 的快速构建的 Web 框架。
npm install express –save
// index.js
require('dotenv').config();
const express = require('express');
const app = express();
const PORT = process.env.PORT || 8888;
// 启动 Express 服务
const c = require('child_process')
const server=app.listen(PORT,()=>{
console.log(' Express 服务启动,正在监听'+ PORT +'端口');
c.exec('start http://localhost:8888/docs')
})
数据库支持
创建数据库文件,设置连接池,创建连接池管理文件。
npm install sqlite3 –save
npm install generic-pool –save
// 跳过预编译,直接源码编译
npm install –build-from-source
// common/database.js
const sqlite3 = require('sqlite3').verbose(); // 引入sqlite3模块
const genericPool = require('generic-pool'); // 引入generic-pool模块
const factory = {
create: function() { // 创建一个数据库实例
return new Promise(function(resolve, reject) {
let db = new sqlite3.Database('./SQLite.db', sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => { // 创建数据库实例
if (err) {
reject(err);
} else {
resolve(db);
}
});
});
},
destroy: function(db) { // 销毁一个数据库实例
return new Promise(function(resolve) {
db.close();
resolve();
});
}
};
const opts = {
max: 10, // 最大连接数
min: 2 // 最小连接数
};
const myPool = genericPool.createPool(factory, opts); // 创建连接池
module.exports = myPool; // 导出连接池
日志管理
你如果要使用日志管理文件,可以安装以下两大模块之一:
- Log4js 提供了模块化的日志记录功能,可以根据需要为不同的模块或功能分配独立的日志级别,从而实现更精细的信息输出控制。
npm install log4js –save
// common/log.js
const log4js = require('log4js');
log4js.configure({
appenders: {
errorFile: { type: 'file', filename: '../Logs/errors.log' },
debugFile: { type: 'file', filename: '../Logs/debugs.log' },
infoFile: { type: 'file', filename: '../Logs/info.log' },
error: { type: 'logLevelFilter', level: 'error', appender: 'errorFile' },
debug: { type: 'logLevelFilter', level: 'debug', appender: 'debugFile', maxLevel: 'debug' },
info: { type: 'logLevelFilter', level: 'info', appender: 'infoFile', maxLevel: 'info' }
},
categories: {
default: { appenders: ['info', 'debug', 'error'], level: 'trace' }
}
});
const logger = log4js.getLogger();
module.exports = logger;
- winston是一个日志记录器,它提供了灵活的配置和灵活的输出功能。winston-daily-rotate-file 是一个基于winston的日志模块,它允许你根据日期自动生成日志文件。
npm install winston –save
npm install winston-daily-rotate-file –save
身份校验
JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。这种信息可以被验证和信任,因为它是数字签名的。JWT可以使用密钥(例如,秘密或公钥/私钥对)进行签名,也可以选择性地加密其包含的信息
npm install jsonwebtoken –save
// common/authenticate.js
const jwt = require('jsonwebtoken');
const SECRET_KEY = process.env.SECRET_KEY; // 密钥
exports.authenticateToken=(req, res, next)=>{
const authHeader = req.headers['authorization']; // 获取请求头中的token
const token = authHeader && authHeader.split(' ')[1];
if (token == null){
return res.status(401).send({status:"401",message:'无权限访问'})
}
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err){
return res.status(403).send({status:"403",message:'token无效或已过期'})
}
/* //临近过期时(前10分钟),刷新token
let Expiration = process.env.Expiration
let exp = user.exp * 1000
let now = new Date().getTime()
console.log(new Date(exp).toLocaleString(),now - exp)
if (exp - now <= 60 * 1000) {
const newToken = jwt.sign({ id: user.UserID, name:user.UserName }, SECRET_KEY, { expiresIn: Expiration });
res.setHeader('Refresh-Authorization',newToken );
}
*/
req.user = user; // 将用户信息存储在请求中
next(); // 继续执行后续操作
});
}
// ../Interface/user
const myPool = require('../common/database.js');
//创建用户,以下的业务代码省略
exports.userCreatePost = async(req,res)=>{
//...
}
// ./Routes/user.js
const express = require('express');
const router = express.Router();
const User = require('../Interface/user')
const {authenticateToken} = require('../common/authenticate')
// 创建用户
router.post('/api/user/create',authenticateToken,(req,res)=>{
User.userCreatePost(req,res)
})
接口文档支持
如果需要使用接口文档,可以使用以下两个方案中的一个:
- express-jsdoc-swagger 能够根据JSDoc注释自动生成接口文档,并且支持 Swagger UI。介绍更详细,对前端更友好,但是需要自己编写注释。
npm install express-jsdoc-swagger –save
npm install swagger-jsdoc –save
// swaggerConfig.js
const options = {
info: {
version: '1.0.0',
title: "node-template",
description:"node-template's interface documentation",
license: {
name: 'MIT',
},
},
security: {
BasicAuth: {
type: 'http',
scheme: 'basic',
},
},
baseDir: __dirname,
filesPattern: './**/*.js',
swaggerUIPath: '/docs',
exposeSwaggerUI: true,
exposeApiDocs: false,
apiDocsPath: '/v3/api-docs',
notRequiredAsNullable: false,
swaggerUiOptions: {},
multiple: true,
};
module.exports = options;
// index.js
const expressJSDocSwagger = require('express-jsdoc-swagger');
const swaggerConfig = require('./swaggerConfig.js')
expressJSDocSwagger(app)(swaggerConfig);
// ./Routes/user.js
/**
* 创建用户的请求对象
* @typedef {object} Register
* @property {string} UserName.required - 用户名称
* @property {string} Email.required - 用户邮箱
*/
/**
* POST /api/user/create
* @summary 用户的创建接口
* @tags user
* @param {Register} request.body - 用户信息 - application/json
* @return {object} 200 - 注册成功 - application/json
* @return 409 - 用户名已存在
* @return 500 - 错误的查询
* @example response - 200 - 注册成功示例 - application/json
* {
* "id": "XXXXXXXXX",
* "UserName": "XXXXXXXXXX",
* "status": "200",
* "message": "用户注册成功"
* }
*/
router.post('/api/user/create',authenticateToken,(req,res)=>{
User.userCreatePost(req,res)
})
- swagger-ui-express 是一个基于express的WebSocket中间件,它允许你轻松地使用WebSocket协议。能根据json文件自动生成接口文档,并且支持 Swagger UI。对后端更轻松,但是文档不好看(我不喜欢这种,不仔细介绍了)。
npm install swagger-ui-express –save
npm install swagger-jsdoc –save
// swagger.json
{
"openapi": "3.0.3",
"info": {
"title": "My API",
"version": "1.0.0"
},
"paths": { //各个接口及相关的内容
"/users": {
"get": { /*...*/ },
"post": { /*...*/ }
}
},
"components": { //请求对象和响应对象
"schemas": {
"User": { /*...*/ }
}
}
}
// index.js
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('./swagger.json');
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
WebSocket
你如果要使用WebSocket,可以安装以下两个模块之一:
- ws是一个Node.js模块,主要用于在服务器端实现WebSocket协议。它允许客户端(通常是浏览器)与服务端建立持久化的连接.
npm install ws –save
// ./common/webSocket.js
const WebSocket = require('ws');
const jwt = require('jsonwebtoken');
const SECRET_KEY = process.env.SECRET_KEY; // 密钥
function verifyToken(token) { //验证token
var isValid = false
var user = null;
jwt.verify(token, SECRET_KEY, (err, _user) => {
if (!err){
isValid = true
user = _user
}
});
return {isValid,user};
}
class WebSocketServer {
constructor(port) {
this.wss = new WebSocket.Server({
port,
verifyClient: (info, done) => {
const token = info.req.headers.authorization || info.req.url.split('?')[1].split('=')[1];
const { isValid, user } = verifyToken(token);
if (isValid) {
info.req.user = user; //将用户信息附加到请求对象上
done(true); //验证通过,允许连接
} else {
done(false, 401, 'Unauthorized'); //验证失败,拒绝连接并返回 401 状态码
}
}
});
this.clients = new Map(); //使用Map来存储客户端连接
this.wss.on('connection', this.onConnection.bind(this));
}
//监听客户端连接
onConnection(ws,req) {
const user = req.user
this.clients.set(user.id, ws);
console.log("WebSocket :"+user.id+"用户已上线");
logger.info("WebSocket :"+user.id+"用户已上线")
ws.on('message', (messageBuffer) => {
//将Buffer解码为UTF-8字符串
const messageString = messageBuffer.toString('utf8');
console.log('接受消息:', messageString);
ws.send('Message received: ' + messageString);
});
//断开连接
ws.on('close', () => {
this.removeClient(ws)
});
}
//广播消息给所有连接的客户端,除了指定的ws
broadcast(message, exceptWs){
this.clients.forEach((client) => {
if (client !== exceptWs && client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
}
//推送给特定人员
sendMessageToUser(userId, message) {
const ws = this.clients.get(userId);
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(message);
}else{
console.log("WebSocket :"+userId+"用户不在线");
logger.info("WebSocket :"+userId+"用户不在线")
}
}
//从连接列表中移除指定的客户端
removeClient(userId) {
const ws = this.clients.get(userId);
if (ws) {
ws.close();
this.clients.delete(userId);
console.log("WebSocket :"+userId+"用户已下线");
logger.info("WebSocket :"+userId+"用户已下线")
}
}
//启动服务
start() {
console.log('WebSocket 服务启动,正在监听'+ this.wss.options.port +'端口');
logger.info('./common/webSocket.js WebSocket服务启动,正在监听'+ this.wss.options.port +'端口');
}
}
const WSPORT = process.env.WSPORT || 8889;
const WSdev = new WebSocketServer(WSPORT);
module.exports = WSdev;
// ./index.js
const WSdev = require('./common/webSocket');
WSdev.start(); //启动 WebSocket 服务
- express-ws是一个针对Node.js开发者的库,它扩展了流行的Express框架,通过添加对WebSocket协议的支持,让开发者能够利用熟悉的Express API轻松地创建和管理WebSocket服务器。(推荐使用)
npm install express-ws –save
中间件数据处理
npm install cors –save
npm install body-parser –save
// index.js
const app = express()
const cors = require('cors')
app.use(cors()) //允许跨域:启动所有Cors请求
const bodyParser = require('body-parser')
app.use(bodyParser.json()); //解析json数据
app.use(bodyParser.urlencoded({extended: false})); //解析form数据
后续扩展
npm install express-session –save
npm install express-jwt –save
npm install express-validator –save
npm install express-winston –save
npm install express-rate-limit –save
npm install express-ipfilter –save
npm install express-ipware –save
npm install express-fileupload –save
npm install express-flash –save
npm install express-handlebars –save
npm install express-graphql –save
npm install express-graphql-auth –save
npm install express-graphql-auth-jwt –save