Skip to content
On this page

Node.js 实战案例

本章通过实际案例展示Node.js在不同场景下的应用,包括完整的项目架构、实现细节和最佳实践。

案例一:RESTful API服务

项目概述

构建一个完整的RESTful API服务,包含用户管理、认证和数据操作功能。

项目结构

user-api/
├── src/
│   ├── controllers/
│   │   ├── user.controller.js
│   │   └── auth.controller.js
│   ├── models/
│   │   ├── user.model.js
│   │   └── index.js
│   ├── routes/
│   │   ├── users.route.js
│   │   └── auth.route.js
│   ├── middleware/
│   │   ├── auth.middleware.js
│   │   ├── validation.middleware.js
│   │   └── error.middleware.js
│   ├── services/
│   │   ├── user.service.js
│   │   └── auth.service.js
│   ├── utils/
│   │   ├── database.js
│   │   └── logger.js
│   └── app.js
├── tests/
├── config/
├── .env
├── package.json
└── README.md

核心代码实现

javascript
// src/app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');

const userRoutes = require('./routes/users.route');
const authRoutes = require('./routes/auth.route');
const errorMiddleware = require('./middleware/error.middleware');
const logger = require('./utils/logger');

const app = express();

// 安全中间件
app.use(helmet());
app.use(cors());

// 速率限制
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100 // 限制每个IP 15分钟内最多100个请求
});
app.use(limiter);

// 解析JSON
app.use(express.json({ limit: '10mb' }));

// 请求日志
app.use(logger.requestLogger);

// 路由
app.use('/api/auth', authRoutes);
app.use('/api/users', userRoutes);

// 健康检查端点
app.get('/health', (req, res) => {
  res.json({ status: 'OK', timestamp: new Date().toISOString() });
});

// 404处理
app.use('*', (req, res) => {
  res.status(404).json({ error: 'Route not found' });
});

// 全局错误处理
app.use(errorMiddleware);

module.exports = app;
javascript
// src/models/user.model.js
const { DataTypes } = require('sequelize');
const sequelize = require('../utils/database');

const User = sequelize.define('User', {
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  name: {
    type: DataTypes.STRING,
    allowNull: false,
    validate: {
      notEmpty: true,
      len: [1, 100]
    }
  },
  email: {
    type: DataTypes.STRING,
    allowNull: false,
    unique: true,
    validate: {
      isEmail: true
    }
  },
  password: {
    type: DataTypes.STRING,
    allowNull: false,
    validate: {
      len: [6, 100]
    }
  },
  role: {
    type: DataTypes.ENUM('user', 'admin', 'moderator'),
    defaultValue: 'user'
  },
  isActive: {
    type: DataTypes.BOOLEAN,
    defaultValue: true
  }
}, {
  tableName: 'users',
  timestamps: true,
  indexes: [
    {
      unique: true,
      fields: ['email']
    }
  ]
});

module.exports = User;
javascript
// src/services/user.service.js
const User = require('../models/user.model');
const bcrypt = require('bcryptjs');

class UserService {
  async createUser(userData) {
    // 检查用户是否已存在
    const existingUser = await User.findOne({
      where: { email: userData.email }
    });
    
    if (existingUser) {
      throw new Error('用户已存在');
    }
    
    // 哈希密码
    const hashedPassword = await bcrypt.hash(userData.password, 12);
    
    // 创建用户
    const user = await User.create({
      ...userData,
      password: hashedPassword
    });
    
    // 返回不包含密码的用户信息
    const { password, ...userWithoutPassword } = user.toJSON();
    return userWithoutPassword;
  }
  
  async getUserById(id) {
    const user = await User.findByPk(id);
    if (!user) {
      throw new Error('用户不存在');
    }
    
    const { password, ...userWithoutPassword } = user.toJSON();
    return userWithoutPassword;
  }
  
  async updateUser(id, updateData) {
    const user = await User.findByPk(id);
    if (!user) {
      throw new Error('用户不存在');
    }
    
    if (updateData.password) {
      updateData.password = await bcrypt.hash(updateData.password, 12);
    }
    
    await user.update(updateData);
    
    const { password, ...userWithoutPassword } = user.toJSON();
    return userWithoutPassword;
  }
  
  async deleteUser(id) {
    const user = await User.findByPk(id);
    if (!user) {
      throw new Error('用户不存在');
    }
    
    await user.destroy();
    return { message: '用户删除成功' };
  }
  
  async getAllUsers(page = 1, limit = 10) {
    const offset = (page - 1) * limit;
    
    const { count, rows } = await User.findAndCountAll({
      attributes: { exclude: ['password'] },
      limit: parseInt(limit),
      offset: parseInt(offset),
      order: [['createdAt', 'DESC']]
    });
    
    return {
      users: rows,
      pagination: {
        page: parseInt(page),
        limit: parseInt(limit),
        total: count,
        pages: Math.ceil(count / limit)
      }
    };
  }
}

module.exports = new UserService();

案例二:实时聊天应用

项目概述

使用Node.js和Socket.IO构建实时聊天应用,支持多房间聊天、用户状态显示等功能。

核心实现

javascript
// chat-server.js
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const cors = require('cors');

const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
  cors: {
    origin: process.env.CLIENT_URL || "http://localhost:3000",
    methods: ["GET", "POST"]
  }
});

// 在线用户管理
const users = new Map();

io.use((socket, next) => {
  const user = socket.handshake.auth;
  if (user && user.id && user.name) {
    socket.userId = user.id;
    socket.userName = user.name;
    next();
  } else {
    next(new Error('Authentication error'));
  }
});

io.on('connection', (socket) => {
  console.log(`用户 ${socket.userName} 已连接`);
  
  // 添加用户到在线列表
  users.set(socket.userId, {
    id: socket.userId,
    name: socket.userName,
    socketId: socket.id,
    joinedAt: new Date()
  });
  
  // 通知其他用户
  socket.broadcast.emit('user_joined', {
    id: socket.userId,
    name: socket.userName
  });
  
  // 更新在线用户列表
  io.emit('users_list', Array.from(users.values()));
  
  // 加入房间
  socket.on('join_room', (room) => {
    socket.join(room);
    socket.to(room).emit('user_joined_room', {
      userId: socket.userId,
      userName: socket.userName,
      room: room
    });
  });
  
  // 处理消息
  socket.on('send_message', (data) => {
    const message = {
      id: Date.now().toString(),
      userId: socket.userId,
      userName: socket.userName,
      message: data.message,
      timestamp: new Date(),
      room: data.room || null
    };
    
    if (data.room) {
      // 房间消息
      io.to(data.room).emit('receive_message', message);
    } else {
      // 全局消息
      io.emit('receive_message', message);
    }
  });
  
  // 用户离开
  socket.on('disconnect', () => {
    console.log(`用户 ${socket.userName} 已断开连接`);
    
    users.delete(socket.userId);
    io.emit('user_left', {
      id: socket.userId,
      name: socket.userName
    });
    io.emit('users_list', Array.from(users.values()));
  });
  
  // 用户状态更新
  socket.on('user_typing', (data) => {
    socket.to(data.room).emit('user_typing', {
      userId: socket.userId,
      userName: socket.userName,
      room: data.room
    });
  });
});

const PORT = process.env.PORT || 4000;
server.listen(PORT, () => {
  console.log(`聊天服务器运行在端口 ${PORT}`);
});

案例三:微服务架构

项目概述

构建基于Node.js的微服务架构,包含用户服务、订单服务和API网关。

API网关实现

javascript
// gateway.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const rateLimit = require('express-rate-limit');

const app = express();

// 速率限制
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100,
  message: '请求过于频繁'
});

app.use(apiLimiter);

// 服务发现和路由
const userProxy = createProxyMiddleware({
  target: 'http://user-service:3001',
  changeOrigin: true,
  pathRewrite: {
    '^/api/users': '/users'
  }
});

const orderProxy = createProxyMiddleware({
  target: 'http://order-service:3002',
  changeOrigin: true,
  pathRewrite: {
    '^/api/orders': '/orders'
  }
});

// 路由
app.use('/api/users', userProxy);
app.use('/api/orders', orderProxy);

// 健康检查
app.get('/health', (req, res) => {
  res.json({ 
    status: 'OK',
    services: {
      user: 'running',
      order: 'running',
      gateway: 'running'
    }
  });
});

app.listen(3000, () => {
  console.log('API网关运行在端口 3000');
});

案例四:文件上传服务

项目概述

构建安全的文件上传服务,支持多格式验证、病毒扫描和云存储集成。

核心实现

javascript
// file-upload-service.js
const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs').promises;
const { v4: uuidv4 } = require('uuid');
const sharp = require('sharp'); // 图片处理

const app = express();

// 配置multer存储
const storage = multer.diskStorage({
  destination: async (req, file, cb) => {
    // 创建按日期组织的目录结构
    const date = new Date();
    const dir = path.join('uploads', 
      date.getFullYear().toString(),
      (date.getMonth() + 1).toString().padStart(2, '0'),
      date.getDate().toString().padStart(2, '0')
    );
    
    try {
      await fs.mkdir(dir, { recursive: true });
      cb(null, dir);
    } catch (err) {
      cb(err);
    }
  },
  filename: (req, file, cb) => {
    // 生成安全的文件名
    const uniqueName = `${uuidv4()}-${Date.now()}${path.extname(file.originalname)}`;
    cb(null, uniqueName);
  }
});

// 文件过滤器
const fileFilter = (req, file, cb) => {
  // 允许的MIME类型
  const allowedMimeTypes = [
    'image/jpeg', 'image/png', 'image/gif', 'image/webp',
    'application/pdf',
    'text/plain',
    'application/msword',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  ];
  
  if (allowedMimeTypes.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error('不支持的文件类型'), false);
  }
};

const upload = multer({
  storage: storage,
  limits: {
    fileSize: 10 * 1024 * 1024, // 10MB
    files: 5 // 最多5个文件
  },
  fileFilter: fileFilter
});

// 图片优化中间件
const optimizeImage = async (req, res, next) => {
  if (!req.file) return next();
  
  const file = req.file;
  
  // 只对图片文件进行优化
  if (file.mimetype.startsWith('image/')) {
    try {
      await sharp(file.path)
        .resize(1920, 1080, { 
          fit: 'inside', 
          withoutEnlargement: true 
        })
        .jpeg({ quality: 80 })
        .toFile(file.path + '.optimized');
      
      // 替换原文件
      await fs.unlink(file.path);
      await fs.rename(file.path + '.optimized', file.path);
      
      req.optimizedFile = file;
    } catch (err) {
      console.error('图片优化失败:', err);
    }
  }
  
  next();
};

// 上传端点
app.post('/upload', 
  upload.array('files', 5), 
  optimizeImage, 
  (req, res) => {
    if (!req.files || req.files.length === 0) {
      return res.status(400).json({ error: '未选择文件' });
    }
    
    const uploadedFiles = req.files.map(file => ({
      originalName: file.originalname,
      filename: file.filename,
      path: file.path,
      size: file.size,
      mimetype: file.mimetype
    }));
    
    res.json({
      message: '文件上传成功',
      files: uploadedFiles
    });
  }
);

// 文件下载
app.get('/download/:filename', async (req, res) => {
  try {
    const filename = req.params.filename;
    const filePath = path.join('uploads', filename);
    
    // 验证文件路径(防止路径遍历)
    const resolvedPath = path.resolve(filePath);
    const baseDir = path.resolve('uploads');
    
    if (!resolvedPath.startsWith(baseDir)) {
      return res.status(403).json({ error: '访问被拒绝' });
    }
    
    await fs.access(filePath);
    res.download(filePath);
  } catch (err) {
    res.status(404).json({ error: '文件未找到' });
  }
});

app.listen(3000, () => {
  console.log('文件上传服务运行在端口 3000');
});

案例五:定时任务服务

项目概述

构建基于Node.js的定时任务服务,支持任务调度、监控和管理。

核心实现

javascript
// task-scheduler.js
const cron = require('node-cron');
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
const fs = require('fs').promises;

class TaskScheduler {
  constructor() {
    this.tasks = new Map();
    this.taskLogs = [];
    this.maxLogEntries = 1000;
  }
  
  // 添加定时任务
  addTask(name, cronExpression, taskFunction, options = {}) {
    const task = cron.schedule(cronExpression, async () => {
      const logEntry = {
        id: Date.now().toString(),
        name: name,
        timestamp: new Date().toISOString(),
        status: 'running'
      };
      
      try {
        console.log(`执行任务: ${name}`);
        await taskFunction();
        logEntry.status = 'completed';
        logEntry.duration = Date.now() - parseInt(logEntry.id);
      } catch (error) {
        logEntry.status = 'failed';
        logEntry.error = error.message;
        console.error(`任务 ${name} 执行失败:`, error);
      }
      
      // 记录日志
      this.taskLogs.push(logEntry);
      if (this.taskLogs.length > this.maxLogEntries) {
        this.taskLogs.shift();
      }
      
      // 保存日志到文件
      this.saveTaskLogs();
    }, {
      scheduled: options.scheduled !== false,
      timezone: options.timezone || 'Asia/Shanghai'
    });
    
    this.tasks.set(name, {
      cron: task,
      expression: cronExpression,
      function: taskFunction,
      options: options
    });
    
    console.log(`任务 ${name} 已添加: ${cronExpression}`);
  }
  
  // 启动任务
  startTask(name) {
    const task = this.tasks.get(name);
    if (task) {
      task.cron.start();
      console.log(`任务 ${name} 已启动`);
    }
  }
  
  // 停止任务
  stopTask(name) {
    const task = this.tasks.get(name);
    if (task) {
      task.cron.stop();
      console.log(`任务 ${name} 已停止`);
    }
  }
  
  // 获取任务状态
  getTaskStatus(name) {
    const task = this.tasks.get(name);
    if (!task) return null;
    
    return {
      name: name,
      expression: task.expression,
      running: task.cron.running,
      options: task.options
    };
  }
  
  // 获取所有任务状态
  getAllTasks() {
    const tasks = [];
    for (const [name, task] of this.tasks) {
      tasks.push(this.getTaskStatus(name));
    }
    return tasks;
  }
  
  // 获取任务日志
  getTaskLogs(limit = 50) {
    return this.taskLogs.slice(-limit).reverse();
  }
  
  // 保存日志到文件
  async saveTaskLogs() {
    try {
      await fs.writeFile('task-logs.json', JSON.stringify(this.taskLogs, null, 2));
    } catch (error) {
      console.error('保存任务日志失败:', error);
    }
  }
  
  // 加载日志
  async loadTaskLogs() {
    try {
      const data = await fs.readFile('task-logs.json', 'utf8');
      this.taskLogs = JSON.parse(data);
    } catch (error) {
      // 文件不存在或解析失败,使用空数组
      this.taskLogs = [];
    }
  }
}

// 初始化任务调度器
const scheduler = new TaskScheduler();

// 示例任务
scheduler.addTask(
  'daily-report',
  '0 9 * * *', // 每天上午9点执行
  async () => {
    console.log('生成每日报告...');
    // 这里放置实际的任务逻辑
    await new Promise(resolve => setTimeout(resolve, 1000));
    console.log('每日报告生成完成');
  },
  { timezone: 'Asia/Shanghai' }
);

scheduler.addTask(
  'cleanup-temp-files',
  '0 */6 * * *', // 每6小时执行
  async () => {
    console.log('清理临时文件...');
    // 清理逻辑
    await new Promise(resolve => setTimeout(resolve, 500));
    console.log('临时文件清理完成');
  }
);

// 启动调度器
async function startScheduler() {
  await scheduler.loadTaskLogs();
  console.log('任务调度器已启动');
}

startScheduler();

// API端点(如果需要)
const express = require('express');
const api = express();

api.get('/tasks', (req, res) => {
  res.json(scheduler.getAllTasks());
});

api.get('/tasks/:name', (req, res) => {
  const status = scheduler.getTaskStatus(req.params.name);
  if (!status) {
    return res.status(404).json({ error: '任务未找到' });
  }
  res.json(status);
});

api.get('/logs', (req, res) => {
  const limit = parseInt(req.query.limit) || 50;
  res.json(scheduler.getTaskLogs(limit));
});

api.listen(3001, () => {
  console.log('任务调度API运行在端口 3001');
});

这些实战案例展示了Node.js在不同场景下的应用,涵盖了从基础API服务到复杂微服务架构的完整实现方案。