← 返回首页

核心特性

高性能异步

基于事件驱动的非阻塞 I/O 模型,单线程高效处理数万并发连接

🧩
中间件机制

灵活的中间件架构,轻松扩展请求处理流程,实现关注点分离

🛣️
路由管理

RESTful 风格路由设计,支持参数解析、正则匹配、路由分组

🔒
安全防护

内置安全中间件,防范 XSS、CSRF、SQL 注入等常见攻击

📦
NPM 生态

拥有全球最大的包管理生态,超过 200 万个开源包可用

🔄
实时通信

原生支持 WebSocket,轻松构建实时聊天、推送等功能

事件循环机制

Node.js 事件循环详解

理解事件循环是掌握 Node.js 的关键。事件循环使得 Node.js 能够执行非阻塞 I/O 操作,尽管 JavaScript 是单线程的。

事件循环阶段
┌───────────────────────────┐ ┌─>│ timers │ // setTimeout, setInterval 回调 │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ pending callbacks │ // I/O 回调(如 TCP 错误) │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ idle, prepare │ // 内部使用 │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ poll │ // 检索新的 I/O 事件 │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ check │ // setImmediate 回调 │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ └──┤ close callbacks │ // socket.on('close', ...) └───────────────────────────┘

执行顺序示例

event-loop-demo.js
console.log('1. 同步代码开始'); setTimeout(() => { console.log('2. setTimeout 回调'); }, 0); setImmediate(() => { console.log('3. setImmediate 回调'); }); Promise.resolve().then(() => { console.log('4. Promise 微任务'); }); process.nextTick(() => { console.log('5. nextTick 回调'); }); console.log('6. 同步代码结束'); // 输出顺序:1 -> 6 -> 5 -> 4 -> 2 -> 3 // nextTick 优先级最高,Promise 次之,然后是宏任务
💡 执行优先级

同步代码 > process.nextTick > Promise.then > setTimeout/setImmediate

Express 中间件

中间件执行流程

Express 中间件是按顺序执行的函数,可以访问请求对象、响应对象和下一个中间件函数。中间件可以执行任何代码、修改请求和响应对象、结束请求-响应循环、调用下一个中间件。

middleware/index.js
// 请求计时中间件 const requestTimer = (req, res, next) => { req.startTime = Date.now(); // 响应完成时记录耗时 res.on('finish', () => { const duration = Date.now() - req.startTime; console.log(`${req.method} ${req.path} - ${duration}ms`); }); next(); }; // 请求体大小限制中间件 const bodyLimit = (maxSize) => (req, res, next) => { let size = 0; req.on('data', (chunk) => { size += chunk.length; if (size > maxSize) { res.status(413).json({ error: '请求体过大' }); } }); next(); }; // 错误处理中间件(必须有4个参数) const errorHandler = (err, req, res, next) => { console.error('错误详情:', err.stack); // 区分操作错误和程序错误 if (err.isOperational) { res.status(err.statusCode).json({ success: false, message: err.message }); } else { // 程序错误,不暴露详情 res.status(500).json({ success: false, message: '服务器内部错误' }); } }; module.exports = { requestTimer, bodyLimit, errorHandler };

常用第三方中间件

中间件 功能 使用示例
express.json() 解析 JSON 请求体 app.use(express.json())
cors 处理跨域请求 app.use(cors({ origin: '*' }))
helmet 设置安全 HTTP 头 app.use(helmet())
morgan HTTP 请求日志 app.use(morgan('combined'))
compression Gzip 压缩响应 app.use(compression())
express-rate-limit 请求频率限制 app.use(rateLimit({ max: 100 }))
express-validator 请求参数验证 body('email').isEmail()

中间件注册顺序

app.js - 推荐的中间件顺序
const express = require('express'); const helmet = require('helmet'); const cors = require('cors'); const compression = require('compression'); const morgan = require('morgan'); const app = express(); // 1. 安全中间件(最先执行) app.use(helmet()); // 2. CORS 处理 app.use(cors()); // 3. 压缩响应 app.use(compression()); // 4. 请求日志 app.use(morgan('dev')); // 5. 请求体解析 app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true })); // 6. 静态文件服务 app.use(express.static('public')); // 7. 业务路由 app.use('/api', apiRouter); // 8. 404 处理 app.use((req, res) => { res.status(404).json({ error: '接口不存在' }); }); // 9. 错误处理(最后执行) app.use(errorHandler);
⚠️ 注意事项

错误处理中间件必须放在最后,且必须有4个参数 (err, req, res, next),否则不会被识别为错误处理中间件。

RESTful 路由设计

RESTful API 设计规范

HTTP 方法 路由示例 操作 幂等性
GET /api/users 获取用户列表
GET /api/users/:id 获取单个用户
POST /api/users 创建用户
PUT /api/users/:id 全量更新用户
PATCH /api/users/:id 部分更新用户
DELETE /api/users/:id 删除用户

路由模块化实现

routes/users.js
const express = require('express'); const { body, param, query } = require('express-validator'); const router = express.Router(); const userController = require('../controllers/userController'); const { authenticate } = require('../middleware/auth'); const { validate } = require('../middleware/validate'); // 获取用户列表(支持分页和搜索) router.get('/', authenticate, [ query('page').optional().isInt({ min: 1 }), query('limit').optional().isInt({ min: 1, max: 100 }), query('search').optional().trim().escape() ], validate, userController.getUsers ); // 获取单个用户 router.get('/:id', authenticate, param('id').isMongoId().withMessage('无效的用户ID'), validate, userController.getUserById ); // 创建用户 router.post('/', [ body('username') .trim() .isLength({ min: 2, max: 20 }) .withMessage('用户名长度为2-20个字符'), body('email') .isEmail() .normalizeEmail() .withMessage('请输入有效的邮箱地址'), body('password') .isLength({ min: 6 }) .withMessage('密码至少6个字符') ], validate, userController.createUser ); // 更新用户 router.put('/:id', authenticate, [ param('id').isMongoId(), body('username').optional().trim().isLength({ min: 2 }), body('email').optional().isEmail() ], validate, userController.updateUser ); // 删除用户 router.delete('/:id', authenticate, param('id').isMongoId(), validate, userController.deleteUser ); module.exports = router;

统一响应格式

utils/response.js
// 成功响应 const success = (res, data, message = '操作成功', statusCode = 200) => { res.status(statusCode).json({ success: true, message, data, timestamp: new Date().toISOString() }); }; // 分页响应 const paginate = (res, { data, page, limit, total }) => { res.json({ success: true, data, pagination: { page: parseInt(page), limit: parseInt(limit), total, totalPages: Math.ceil(total / limit), hasNext: page * limit < total, hasPrev: page > 1 } }); }; // 错误响应 const error = (res, message, statusCode = 400, errors = null) => { res.status(statusCode).json({ success: false, message, errors, timestamp: new Date().toISOString() }); }; module.exports = { success, paginate, error };

异步编程模式

async/await 最佳实践

controllers/userController.js
const User = require('../models/User'); const { success, error } = require('../utils/response'); // 使用 asyncHandler 包装,自动捕获错误 const asyncHandler = (fn) => (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next); }; // 获取用户列表 exports.getUsers = asyncHandler(async (req, res) => { const { page = 1, limit = 10, search } = req.query; // 构建查询条件 const query = {}; if (search) { query.$or = [ { username: { $regex: search, $options: 'i' } }, { email: { $regex: search, $options: 'i' } } ]; } // 并行执行查询和计数 const [users, total] = await Promise.all([ User.find(query) .select('-password') .sort({ createdAt: -1 }) .skip((page - 1) * limit) .limit(limit) .lean(), User.countDocuments(query) ]); success(res, { users, total, page, limit }); }); // 批量操作示例 exports.batchUpdate = asyncHandler(async (req, res) => { const { userIds, updates } = req.body; // 使用 Promise.allSettled 处理部分失败的情况 const results = await Promise.allSettled( userIds.map(id => User.findByIdAndUpdate(id, updates, { new: true }) ) ); const succeeded = results.filter(r => r.status === 'fulfilled'); const failed = results.filter(r => r.status === 'rejected'); success(res, { updated: succeeded.length, failed: failed.length, errors: failed.map(f => f.reason.message) }); });

并发控制

utils/concurrency.js
// 限制并发数量的批量处理 async function batchProcess(items, processor, concurrency = 5) { const results = []; for (let i = 0; i < items.length; i += concurrency) { const batch = items.slice(i, i + concurrency); const batchResults = await Promise.all( batch.map(item => processor(item)) ); results.push(...batchResults); } return results; } // 使用示例:批量发送邮件 const users = await User.find({ subscribed: true }); await batchProcess(users, async (user) => { await sendEmail(user.email, '通知', content); }, 10); // 每次最多并发10个

安全最佳实践

JWT 身份认证

middleware/auth.js
const jwt = require('jsonwebtoken'); const { AppError } = require('./errorHandler'); // 生成 Token const generateToken = (userId) => { return jwt.sign( { userId }, process.env.JWT_SECRET, { expiresIn: '7d' } ); }; // 验证 Token 中间件 const authenticate = async (req, res, next) => { try { // 从 Header 获取 Token const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { throw new AppError('未提供认证令牌', 401); } const token = authHeader.split(' ')[1]; // 验证 Token const decoded = jwt.verify(token, process.env.JWT_SECRET); // 查询用户(可选:检查用户是否仍然存在) const user = await User.findById(decoded.userId).select('-password'); if (!user) { throw new AppError('用户不存在', 401); } req.user = user; next(); } catch (err) { if (err.name === 'JsonWebTokenError') { return next(new AppError('无效的认证令牌', 401)); } if (err.name === 'TokenExpiredError') { return next(new AppError('认证令牌已过期', 401)); } next(err); } }; // 权限检查中间件 const authorize = (...roles) => { return (req, res, next) => { if (!roles.includes(req.user.role)) { return next(new AppError('没有权限执行此操作', 403)); } next(); }; }; module.exports = { generateToken, authenticate, authorize };

安全配置清单

  • 使用 HTTPS - 生产环境必须启用 SSL/TLS 加密
  • 设置安全 HTTP 头 - 使用 helmet 中间件自动配置
  • 防止 SQL/NoSQL 注入 - 使用参数化查询,验证输入
  • 防止 XSS 攻击 - 转义用户输入,设置 CSP 头
  • 防止 CSRF 攻击 - 使用 CSRF Token 或 SameSite Cookie
  • 限制请求频率 - 使用 express-rate-limit 防止暴力攻击
  • 安全存储密码 - 使用 bcrypt 进行哈希加盐
  • 隐藏技术栈信息 - 移除 X-Powered-By 头

性能优化

缓存策略

middleware/cache.js
const redis = require('redis'); const client = redis.createClient(); // 缓存中间件 const cache = (duration) => { return async (req, res, next) => { const key = `cache:${req.originalUrl}`; try { const cached = await client.get(key); if (cached) { return res.json(JSON.parse(cached)); } // 重写 res.json 方法以缓存响应 const originalJson = res.json.bind(res); res.json = async (data) => { await client.setEx(key, duration, JSON.stringify(data)); return originalJson(data); }; next(); } catch (err) { next(); } }; }; // 使用示例 router.get('/popular', cache(300), controller.getPopular);

性能优化技巧

🗜️
启用 Gzip 压缩

使用 compression 中间件压缩响应体,减少传输大小

📊
数据库索引

为常用查询字段创建索引,使用 explain() 分析查询

🔄
连接池复用

复用数据库连接,避免频繁创建销毁连接

📦
使用 lean()

查询时使用 lean() 返回纯 JS 对象,减少内存占用

项目目录结构

推荐的项目架构

目录结构
server/ ├── src/ │ ├── app.js # Express 应用入口 │ ├── server.js # HTTP 服务器启动 │ │ │ ├── config/ # 配置文件 │ │ ├── index.js # 配置聚合 │ │ ├── database.js # 数据库配置 │ │ └── redis.js # Redis 配置 │ │ │ ├── models/ # 数据模型层 │ │ ├── User.js │ │ ├── Post.js │ │ └── index.js # 模型聚合导出 │ │ │ ├── routes/ # 路由定义 │ │ ├── index.js # 路由聚合 │ │ ├── auth.js │ │ ├── users.js │ │ └── posts.js │ │ │ ├── controllers/ # 控制器层(处理请求) │ │ ├── authController.js │ │ ├── userController.js │ │ └── postController.js │ │ │ ├── services/ # 业务逻辑层 │ │ ├── authService.js │ │ ├── userService.js │ │ └── emailService.js │ │ │ ├── middleware/ # 自定义中间件 │ │ ├── auth.js # 认证中间件 │ │ ├── validate.js # 验证中间件 │ │ ├── errorHandler.js # 错误处理 │ │ └── rateLimiter.js # 限流中间件 │ │ │ ├── utils/ # 工具函数 │ │ ├── logger.js # 日志工具 │ │ ├── response.js # 响应格式化 │ │ ├── crypto.js # 加密工具 │ │ └── validators.js # 自定义验证器 │ │ │ └── jobs/ # 定时任务 │ └── cleanup.js │ ├── tests/ # 测试文件 │ ├── unit/ │ └── integration/ │ ├── public/ # 静态文件 ├── logs/ # 日志文件 ├── .env.example # 环境变量示例 ├── .gitignore ├── package.json ├── Dockerfile └── docker-compose.yml
💡 架构原则

遵循单一职责原则:Routes 定义路由 → Controllers 处理请求 → Services 实现业务 → Models 操作数据。每层只关注自己的职责,便于测试和维护。