引言:为什么选择 Node.js

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,它让开发者能够使用 JavaScript 编写服务器端代码。对于零基础小白来说,Node.js 的最大优势在于:前后端语言统一。如果你已经熟悉前端 JavaScript,那么学习 Node.js 将事半功倍。

Node.js 的核心特点包括:

  • 非阻塞 I/O 模型:能够高效处理大量并发请求
  • 单线程事件循环:简化了并发编程模型
  • 庞大的生态系统:npm 上有超过 200 万个开源包
  • 跨平台支持:可在 Windows、macOS、Linux 上运行

第一部分:环境搭建与基础入门

1.1 安装 Node.js

访问 Node.js 官网 下载 LTS(长期支持)版本。安装完成后,打开终端验证:

# 检查 Node.js 版本 node -v # 输出示例:v18.18.0 # 检查 npm 版本 npm -v # 输出示例:9.8.1 

1.2 第一个 Node.js 程序

创建一个名为 hello.js 的文件:

// 导入内置的 http 模块 const http = require('http'); // 创建 HTTP 服务器 const server = http.createServer((req, res) => { // 设置响应头 res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); // 发送响应内容 res.end('你好,Node.js!这是你的第一个服务器程序。n'); }); // 监听 3000 端口 server.listen(3000, () => { console.log('服务器运行在 http://localhost:3000'); }); 

运行程序:

node hello.js # 输出:服务器运行在 http://localhost:3000 

打开浏览器访问 http://localhost:3000,你将看到欢迎信息。

1.3 模块系统详解

Node.js 采用 CommonJS 模块规范,每个文件都是一个独立的模块。

内置模块示例:

// 1. fs 文件系统模块 const fs = require('fs'); // 异步读取文件 fs.readFile('example.txt', 'utf8', (err, data) => { if (err) { console.error('读取文件失败:', err); return; } console.log('文件内容:', data); }); // 同步读取文件(会阻塞) try { const content = fs.readFileSync('example.txt', 'utf8'); console.log('同步读取:', content); } catch (err) { console.error('同步读取失败:', err); } // 2. path 路径处理模块 const path = require('path'); const fullPath = path.join(__dirname, 'uploads', 'images', 'logo.png'); console.log('完整路径:', fullPath); // 3. events 事件模块 const EventEmitter = require('events'); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on('userLogin', (username) => { console.log(`用户 ${username} 登录成功`); }); myEmitter.emit('userLogin', '张三'); 

第二部分:Express.js 框架入门

2.1 为什么需要框架?

原生 Node.js 开发 Web 应用较为繁琐,Express.js 提供了更简洁的 API 和中间件机制。

2.2 创建 Express 应用

首先安装 Express:

npm init -y npm install express 

创建基础服务器:

const express = require('express'); const app = express(); const PORT = 3000; // 中间件:解析 JSON 请求体 app.use(express.json()); // 中间件:解析 URL 编码的表单数据 app.use(express.urlencoded({ extended: true })); // 中间件:日志记录 app.use((req, res, next) => { console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`); next(); }); // 路由:GET 请求 app.get('/', (req, res) => { res.send('欢迎来到 Express 应用!'); }); // 路由:带参数的 GET 请求 app.get('/users/:id', (req, res) => { const userId = req.params.id; res.json({ message: `获取用户 ${userId} 的信息`, data: { id: userId, name: '张三', age: 25 } }); }); // 路由:POST 请求 app.post('/users', (req, res) => { const { name, email } = req.body; // 数据验证 if (!name || !email) { return res.status(400).json({ error: '姓名和邮箱不能为空' }); } res.status(201).json({ message: '用户创建成功', data: { id: Date.now(), name, email } }); }); // 错误处理中间件 app.use((err, req, res, next) => { console.error(err.stack); res.status(500).json({ error: '服务器内部错误' }); }); // 启动服务器 app.listen(PORT, () => { console.log(`Express 服务器运行在 http://localhost:${PORT}`); }); 

2.3 路由模块化

随着应用规模增长,需要将路由拆分到不同文件:

routes/users.js

const express = require('express'); const router = express.Router(); // GET /api/users - 获取所有用户 router.get('/', (req, res) => { res.json([ { id: 1, name: '张三' }, { id: 2, name: '李四' } ]); }); // POST /api/users - 创建用户 router.post('/', (req, res) => { const { name, email } = req.body; res.status(201).json({ id: Date.now(), name, email }); }); // GET /api/users/:id - 获取单个用户 router.get('/:id', (req, res) => { res.json({ id: req.params.id, name: '张三' }); }); // PUT /api/users/:id - 更新用户 router.put('/:id', (req, res) => { res.json({ id: req.params.id, ...req.body }); }); // DELETE /api/users/:id - 删除用户 router.delete('/:id', (req, res) => { res.json({ message: `用户 ${req.params.id} 已删除` }); }); module.exports = router; 

app.js

const express = require('express'); const app = express(); const userRouter = require('./routes/users'); app.use(express.json()); app.use('/api/users', userRouter); app.listen(3000, () => { console.log('服务器启动成功'); }); 

第三部分:数据库操作(以 MongoDB 为例)

3.1 安装 MongoDB 和 Mongoose

# 安装 MongoDB(macOS) brew install mongodb-community # 安装 Mongoose ODM npm install mongoose 

3.2 连接数据库

const mongoose = require('mongoose'); // 连接 MongoDB mongoose.connect('mongodb://localhost:27017/myapp', { useNewUrlParser: true, useUnifiedTopology: true }); // 连接成功/失败提示 mongoose.connection.on('connected', () => { console.log('MongoDB 连接成功'); }); mongoose.connection.on('error', (err) => { console.error('MongoDB 连接失败:', err); }); 

3.3 定义数据模型

const userSchema = new mongoose.Schema({ name: { type: String, required: [true, '用户名不能为空'], trim: true, minlength: [2, '用户名至少2个字符'], maxlength: [20, '用户名最多20个字符'] }, email: { type: String, required: [true, '邮箱不能为空'], unique: true, match: [/^S+@S+.S+$/, '邮箱格式不正确'] }, age: { type: Number, min: [0, '年龄不能为负数'], max: [150, '年龄不能超过150'] }, createdAt: { type: Date, default: Date.now } }); // 添加自定义验证 userSchema.path('name').validate(function (value) { return value !== 'admin'; }, '用户名不能是 admin'); // 创建模型 const User = mongoose.model('User', userSchema); 

3.4 CRUD 操作示例

// 1. 创建用户(Create) async function createUser() { try { const user = new User({ name: '张三', email: 'zhangsan@example.com', age: 25 }); const savedUser = await user.save(); console.log('用户创建成功:', savedUser); } catch (err) { console.error('创建失败:', err.message); } } // 2. 查询用户(Read) async function findUsers() { try { // 查询所有用户 const allUsers = await User.find({}); console.log('所有用户:', allUsers); // 条件查询 const adultUsers = await User.find({ age: { $gte: 18 } }); console.log('成年用户:', adultUsers); // 分页查询 const page = 1; const limit = 10; const users = await User.find({}) .skip((page - 1) * limit) .limit(limit) .sort({ createdAt: -1 }); console.log('分页用户:', users); } catch (err) { console.error('查询失败:', err.message); } } // 3. 更新用户(Update) async function updateUser() { try { // 方法一:findOneAndUpdate const updatedUser = await User.findOneAndUpdate( { email: 'zhangsan@example.com' }, { $set: { age: 26 } }, { new: true, runValidators: true } ); console.log('更新后的用户:', updatedUser); // 方法二:先查后改 const user = await User.findOne({ email: 'zhangsan@example.com' }); if (user) { user.age = 27; await user.save(); console.log('用户年龄更新为27'); } } catch (err) { console.error('更新失败:', err.message); } } // 4. 删除用户(Delete) async function deleteUser() { try { // 删除单个用户 const result = await User.deleteOne({ email: 'zhangsan@example.com' }); console.log('删除结果:', result); // 删除多个用户 const multiResult = await User.deleteMany({ age: { $lt: 18 } }); console.log('批量删除结果:', multiResult); } catch (err) { console.error('删除失败:', err.message); } } 

3.5 集成到 Express 路由

// routes/users.js const express = require('express'); const router = express.Router(); const User = require('../models/User'); // 创建用户 router.post('/', async (req, res) => { try { const user = new User(req.body); const savedUser = await user.save(); res.status(201).json(savedUser); } catch (err) { // 处理验证错误 if (err.name === 'ValidationError') { const errors = Object.values(err.errors).map(e => e.message); return res.status(400).json({ error: errors.join(', ') }); } // 处理重复键错误 if (err.code === 11000) { return res.status(409).json({ error: '邮箱已存在' }); } res.status(500).json({ error: '服务器错误' }); } }); // 获取用户列表 router.get('/', async (req, res) => { try { const { page = 1, limit = 10, search } = req.query; const query = {}; if (search) { query.name = { $regex: search, $options: 'i' }; } const users = await User.find(query) .skip((page - 1) * limit) .limit(Number(limit)) .sort({ createdAt: -1 }); const total = await User.countDocuments(query); res.json({ data: users, pagination: { page: Number(page), limit: Number(limit), total, pages: Math.ceil(total / limit) } }); } catch (err) { res.status(500).json({ error: '查询失败' }); } }); // 获取单个用户 router.get('/:id', async (req, res) => { try { const user = await User.findById(req.params.id); if (!user) { return res.status(404).json({ error: '用户不存在' }); } res.json(user); } catch (err) { if (err.name === 'CastError') { return res.status(400).json({ error: '无效的用户ID格式' }); } res.status(500).json({ error: '查询失败' }); } }); // 更新用户 router.put('/:id', async (req, res) => { try { const user = await User.findByIdAndUpdate( req.params.id, req.body, { new: true, runValidators: true } ); if (!user) { return res.status(404).json({ error: '用户不存在' }); } res.json(user); } catch (err) { if (err.name === 'ValidationError') { const errors = Object.values(err.errors).map(e => e.message); return res.status(400).json({ error: errors.join(', ') }); } res.status(500).json({ error: '更新失败' }); } }); // 删除用户 router.delete('/:id', async (req, res) => { try { const user = await User.findByIdAndDelete(req.params.id); if (!user) { return res.status(404).json({ error: '用户不存在' }); } res.json({ message: '用户删除成功' }); } catch (err) { res.status(500).json({ error: '删除失败' }); } }); module.exports = router; 

第四部分:中间件与请求处理

4.1 中间件概念

中间件是 Express 的核心概念,本质上是一个函数,可以访问请求对象(req)、响应对象(res)和 next 函数。

// 自定义日志中间件 const requestLogger = (req, res, next) => { const start = Date.now(); // 监听响应完成事件 res.on('finish', () => { const duration = Date.now() - start; console.log(`${req.method} ${req.path} - ${res.statusCode} - ${duration}ms`); }); next(); // 继续执行下一个中间件 }; // 认证中间件 const authenticate = (req, res, next) => { const token = req.headers.authorization; if (!token) { return res.status(401).json({ error: '未提供认证令牌' }); } // 验证 token(简化示例) if (token === 'Bearer valid-token') { req.user = { id: 1, name: '张三' }; // 将用户信息附加到请求对象 next(); } else { res.status(401).json({ error: '无效的认证令牌' }); } }; // 权限中间件 const requireAdmin = (req, res, next) => { if (req.user && req.user.role === 'admin') { next(); } else { res.status(403).json({ error: '需要管理员权限' }); } }; // 使用中间件 app.use(requestLogger); // 受保护的路由 app.get('/admin/dashboard', authenticate, requireAdmin, (req, res) => { res.json({ message: '欢迎管理员', user: req.user }); }); 

4.2 错误处理中间件

// 错误处理中间件(必须放在所有路由之后) app.use((err, req, res, next) => { // 记录错误日志 console.error('错误详情:', { message: err.message, stack: err.stack, url: req.url, method: req.method, timestamp: new Date().toISOString() }); // 根据错误类型返回不同响应 if (err.name === 'ValidationError') { return res.status(400).json({ error: '数据验证失败', details: err.message }); } if (err.name === 'CastError') { return res.status(400).json({ error: '无效的ID格式' }); } if (err.code === 11000) { return res.status(409).json({ error: '数据重复' }); } // 默认错误响应 res.status(err.status || 500).json({ error: process.env.NODE_ENV === 'production' ? '服务器错误' : err.message, ...(process.env.NODE_ENV === 'development' && { stack: err.stack }) }); }); 

第五部分:常见报错难题与解决方案

5.1 模块导入错误

错误信息:

Error: Cannot find module 'express' 

解决方案:

# 1. 检查是否在项目目录 pwd # 2. 初始化 package.json(如果不存在) npm init -y # 3. 安装依赖 npm install express # 4. 检查 node_modules 是否存在 ls node_modules # 5. 如果问题依旧,删除重装 rm -rf node_modules package-lock.json npm install 

5.2 端口占用错误

错误信息:

Error: listen EADDRINUSE: address already in use :::3000 

解决方案:

// 方法一:自动更换端口 const portfinder = require('portfinder'); portfinder.getPort((err, port) => { if (err) { console.error('查找端口失败:', err); return; } app.listen(port, () => { console.log(`服务器运行在 http://localhost:${port}`); }); }); // 方法二:手动查找并终止占用进程 // macOS/Linux lsof -i :3000 kill -9 <PID> // Windows netstat -ano | findstr :3000 taskkill /PID <PID> /F 

5.3 异步错误处理

错误示例:

// ❌ 错误:回调地狱,错误处理复杂 app.get('/users/:id', (req, res) => { User.findById(req.params.id, (err, user) => { if (err) { res.status(500).json({ error: '查询失败' }); return; } Post.find({ userId: user._id }, (err, posts) => { if (err) { res.status(500).json({ error: '查询失败' }); return; } res.json({ user, posts }); }); }); }); 

正确做法:

// ✅ 使用 async/await + try/catch app.get('/users/:id/posts', async (req, res) => { try { const user = await User.findById(req.params.id); if (!user) { return res.status(404).json({ error: '用户不存在' }); } const posts = await Post.find({ userId: user._id }); res.json({ user, posts }); } catch (err) { // 统一错误处理 if (err.name === 'CastError') { return res.status(400).json({ error: '无效的用户ID' }); } res.status(500).json({ error: '服务器错误' }); } }); // ✅ 使用 Promise.all 并行查询 app.get('/users/:id/dashboard', async (req, res) => { try { const [user, posts, comments] = await Promise.all([ User.findById(req.params.id), Post.find({ userId: req.params.id }), Comment.find({ userId: req.params.id }) ]); if (!user) { return res.status(404).json({ error: '用户不存在' }); } res.json({ user, posts, comments }); } catch (err) { res.status(500).json({ error: '服务器错误' }); } }); 

5.4 数据库连接失败

错误信息:

MongooseServerSelectionError: getaddrinfo ENOTFOUND localhost 

解决方案:

// 1. 检查 MongoDB 服务是否运行 // macOS/Linux brew services list | grep mongodb // 或 sudo systemctl status mongod // 2. 连接字符串配置 const mongoose = require('mongoose'); // 开发环境 const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/myapp'; // 生产环境(推荐) // const MONGODB_URI = 'mongodb://username:password@host:port/database?authSource=admin'; mongoose.connect(MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true, maxPoolSize: 10, // 连接池大小 serverSelectionTimeoutMS: 5000, // 超时时间 socketTimeoutMS: 45000, }) .then(() => console.log('MongoDB 连接成功')) .catch(err => { console.error('MongoDB 连接失败:', err.message); // 退出进程 process.exit(1); }); // 3. 重连机制 mongoose.connection.on('disconnected', () => { console.log('MongoDB 断开连接,尝试重连...'); }); mongoose.connection.on('error', (err) => { console.error('MongoDB 连接错误:', err); }); 

5.5 请求体解析错误

错误信息:

SyntaxError: Unexpected token u in JSON at position 0 

解决方案:

// 1. 确保使用了 body-parser 中间件 app.use(express.json({ limit: '10mb' })); // 限制请求体大小 app.use(express.urlencoded({ extended: true, limit: '10mb' })); // 2. 添加错误处理中间件 app.use((err, req, res, next) => { if (err instanceof SyntaxError && err.status === 400 && 'body' in err) { return res.status(400).json({ error: '无效的JSON格式' }); } next(err); }); // 3. 前端请求示例(正确格式) const axios = require('axios'); axios.post('http://localhost:3000/api/users', { name: '张三', email: 'zhangsan@example.com' }, { headers: { 'Content-Type': 'application/json' } }); 

5.6 跨域错误(CORS)

错误信息:

Access to fetch from 'http://localhost:3000' has been blocked by CORS policy 

解决方案:

npm install cors 
const cors = require('cors'); // 基础配置 app.use(cors()); // 生产环境推荐配置 const corsOptions = { origin: process.env.FRONTEND_URL || 'http://localhost:3001', // 允许的前端域名 methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'], credentials: true, // 允许携带 cookie maxAge: 86400 // 预检请求缓存时间(秒) }; app.use(cors(corsOptions)); // 特定路由的 CORS app.use('/api', cors(corsOptions), apiRouter); 

5.7 环境变量未定义

错误信息:

TypeError: Cannot read property 'MONGODB_URI' of undefined 

解决方案:

# 安装 dotenv npm install dotenv 
// 在应用入口文件顶部加载 require('dotenv').config(); // .env 文件(不要提交到 Git) # .env NODE_ENV=development PORT=3000 MONGODB_URI=mongodb://localhost:27017/myapp JWT_SECRET=your-super-secret-key FRONTEND_URL=http://localhost:3001 // 使用环境变量 const PORT = process.env.PORT || 3000; const MONGODB_URI = process.env.MONGODB_URI; if (!MONGODB_URI) { console.error('错误:MONGODB_URI 未配置'); process.exit(1); } 

5.8 文件上传错误

错误信息:

Error: Request entity too large 

解决方案:

const multer = require('multer'); const path = require('path'); // 配置 multer const storage = multer.diskStorage({ destination: (req, file, cb) => { const uploadDir = path.join(__dirname, 'uploads'); // 确保目录存在 if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir, { recursive: true }); } cb(null, uploadDir); }, filename: (req, file, cb) => { // 生成唯一文件名 const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); const ext = path.extname(file.originalname); cb(null, file.fieldname + '-' + uniqueSuffix + ext); } }); // 文件过滤器 const fileFilter = (req, file, cb) => { const allowedTypes = /jpeg|jpg|png|gif|pdf|doc|docx/; const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase()); const mimetype = allowedTypes.test(file.mimetype); if (extname && mimetype) { return cb(null, true); } else { cb(new Error('只支持上传图片和文档文件')); } }; const upload = multer({ storage: storage, limits: { fileSize: 5 * 1024 * 1024 // 5MB }, fileFilter: fileFilter }); // 路由:单文件上传 app.post('/upload/single', upload.single('file'), (req, res) => { if (!req.file) { return res.status(400).json({ error: '未上传文件' }); } res.json({ message: '文件上传成功', file: { filename: req.file.filename, size: req.file.size, mimetype: req.file.mimetype } }); }); // 路由:多文件上传 app.post('/upload/multiple', upload.array('files', 5), (req, res) => { if (!req.files || req.files.length === 0) { return res.status(400).json({ error: '未上传文件' }); } res.json({ message: '文件上传成功', files: req.files.map(f => ({ filename: f.filename, size: f.size })) }); }); // 错误处理 app.use((err, req, res, next) => { if (err instanceof multer.MulterError) { if (err.code === 'LIMIT_FILE_SIZE') { return res.status(400).json({ error: '文件大小不能超过5MB' }); } if (err.code === 'LIMIT_FILE_COUNT') { return res.status(400).json({ error: '一次最多上传5个文件' }); } } next(err); }); 

第六部分:生产环境最佳实践

6.1 使用 PM2 进程管理

# 安装 PM2 npm install -g pm2 # 启动应用 pm2 start app.js --name "my-app" # 常用命令 pm2 list # 查看进程列表 pm2 logs my-app # 查看日志 pm2 restart my-app # 重启 pm2 stop my-app # 停止 pm2 monit # 监控 # 生成配置文件 pm2 ecosystem 

ecosystem.config.js

module.exports = { apps: [{ name: 'my-app', script: 'app.js', instances: 'max', // 根据CPU核心数自动扩展 exec_mode: 'cluster', env: { NODE_ENV: 'production', PORT: 3000 }, env_production: { NODE_ENV: 'production', PORT: 3000 }, error_file: './logs/err.log', out_file: './logs/out.log', log_file: './logs/combined.log', time: true, max_memory_restart: '1G', // 内存超过1GB自动重启 watch: false, ignore_watch: ['node_modules', 'logs', 'uploads'] }] }; 

6.2 日志记录

npm install winston 
const winston = require('winston'); const path = require('path'); // 创建日志实例 const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), transports: [ // 错误日志 new winston.transports.File({ filename: path.join(__dirname, 'logs', 'error.log'), level: 'error', maxsize: 5242880, // 5MB maxFiles: 5 }), // 所有日志 new winston.transports.File({ filename: path.join(__dirname, 'logs', 'combined.log'), maxsize: 5242880, maxFiles: 5 }) ] }); // 开发环境同时输出到控制台 if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), winston.format.simple() ) })); } // 使用日志 logger.info('用户登录成功', { userId: 123, ip: '192.168.1.1' }); logger.error('数据库连接失败', { error: err.message }); logger.warn('内存使用率过高', { memory: process.memoryUsage() }); // Express 中使用 app.use((req, res, next) => { logger.info('请求开始', { method: req.method, url: req.url, ip: req.ip, userAgent: req.get('User-Agent') }); next(); }); 

6.3 安全最佳实践

npm install helmet rate-limit express-rate-limit 
const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); // 1. Helmet 设置安全响应头 app.use(helmet()); // 2. 速率限制 const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15分钟 max: 100, // 每个IP最多100次请求 message: '请求过于频繁,请稍后再试', standardHeaders: true, legacyHeaders: false, // 白名单 skip: (req) => { return req.ip === '127.0.0.1'; } }); app.use('/api/', limiter); // 3. 输入验证 const { body, validationResult } = require('express-validator'); app.post('/api/users', [ body('email').isEmail().normalizeEmail(), body('password').isLength({ min: 6 }), body('name').trim().escape() // 防止XSS ], (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } // 处理请求... }); // 4. 防止 NoSQL 注入 app.use((req, res, next) => { const sanitize = (obj) => { for (const key in obj) { if (obj[key] instanceof Object && !Array.isArray(obj[key])) { sanitize(obj[key]); } if (key === '$ne' || key === '$gt' || key === '$regex') { delete obj[key]; } } }; sanitize(req.body); sanitize(req.query); next(); }); 

6.4 配置管理

npm install config 

config/default.json

{ "server": { "port": 3000, "host": "localhost" }, "database": { "uri": "mongodb://localhost:27017/myapp", "options": { "useNewUrlParser": true, "useUnifiedTopology": true } }, "jwt": { "secret": "your-secret-key", "expiresIn": "7d" } } 

config/production.json

{ "server": { "port": 8080, "host": "0.0.0.0" }, "database": { "uri": "mongodb://prod-host:27017/prod-app" } } 

使用配置:

const config = require('config'); const serverConfig = config.get('server'); const dbConfig = config.get('database'); app.listen(serverConfig.port, serverConfig.host, () => { console.log(`服务器运行在 ${serverConfig.host}:${serverConfig.port}`); }); 

第七部分:实战项目示例

7.1 完整的用户管理系统

// app.js const express = require('express'); const mongoose = require('mongoose'); const cors = require('cors'); const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); require('dotenv').config(); const app = express(); // 中间件 app.use(helmet()); app.use(cors({ origin: process.env.FRONTEND_URL || 'http://localhost:3001', credentials: true })); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true })); // 速率限制 const limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100, message: '请求过于频繁' }); app.use('/api/', limiter); // 连接数据库 mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('MongoDB 连接成功')) .catch(err => { console.error('MongoDB 连接失败:', err); process.exit(1); }); // 用户模型 const userSchema = new mongoose.Schema({ name: { type: String, required: true, trim: true }, email: { type: String, required: true, unique: true }, password: { type: String, required: true, minlength: 6 }, role: { type: String, enum: ['user', 'admin'], default: 'user' }, createdAt: { type: Date, default: Date.now } }); const User = mongoose.model('User', userSchema); // 认证中间件 const authenticate = async (req, res, next) => { try { const token = req.headers.authorization?.replace('Bearer ', ''); if (!token) { return res.status(401).json({ error: '未提供认证令牌' }); } // 实际项目中应使用 JWT 验证 const user = await User.findOne({ _id: token }); if (!user) { return res.status(401).json({ error: '无效的令牌' }); } req.user = user; next(); } catch (err) { res.status(500).json({ error: '认证失败' }); } }; // 路由 app.post('/api/register', async (req, res) => { try { const { name, email, password } = req.body; // 验证输入 if (!name || !email || !password) { return res.status(400).json({ error: '缺少必要字段' }); } // 检查邮箱是否已存在 const existingUser = await User.findOne({ email }); if (existingUser) { return res.status(409).json({ error: '邮箱已注册' }); } // 创建用户(实际项目中应加密密码) const user = new User({ name, email, password }); await user.save(); res.status(201).json({ message: '注册成功', userId: user._id }); } catch (err) { if (err.name === 'ValidationError') { const errors = Object.values(err.errors).map(e => e.message); return res.status(400).json({ error: errors.join(', ') }); } res.status(500).json({ error: '注册失败' }); } }); app.post('/api/login', async (req, res) => { try { const { email, password } = req.body; const user = await User.findOne({ email, password }); if (!user) { return res.status(401).json({ error: '邮箱或密码错误' }); } // 实际项目中应生成 JWT token res.json({ message: '登录成功', token: user._id, user: { id: user._id, name: user.name, role: user.role } }); } catch (err) { res.status(500).json({ error: '登录失败' }); } }); app.get('/api/users', authenticate, async (req, res) => { try { // 只有管理员可以查看所有用户 if (req.user.role !== 'admin') { return res.status(403).json({ error: '需要管理员权限' }); } const users = await User.find({}, '-password').sort({ createdAt: -1 }); res.json(users); } catch (err) { res.status(500).json({ error: '查询失败' }); } }); app.get('/api/profile', authenticate, async (req, res) => { try { const user = await User.findById(req.user._id, '-password'); res.json(user); } catch (err) { res.status(500).json({ error: '查询失败' }); } }); app.put('/api/profile', authenticate, async (req, res) => { try { const { name } = req.body; const user = await User.findByIdAndUpdate( req.user._id, { name }, { new: true, runValidators: true } ).select('-password'); res.json({ message: '更新成功', user }); } catch (err) { if (err.name === 'ValidationError') { const errors = Object.values(err.errors).map(e => e.message); return res.status(400).json({ error: errors.join(', ') }); } res.status(500).json({ error: '更新失败' }); } }); app.delete('/api/users/:id', authenticate, async (req, res) => { try { if (req.user.role !== 'admin') { return res.status(403).json({ error: '需要管理员权限' }); } const user = await User.findByIdAndDelete(req.params.id); if (!user) { return res.status(404).json({ error: '用户不存在' }); } res.json({ message: '用户已删除' }); } catch (err) { res.status(500).json({ error: '删除失败' }); } }); // 错误处理中间件 app.use((err, req, res, next) => { console.error('错误:', err.message); if (err.code === 11000) { return res.status(409).json({ error: '数据重复' }); } res.status(err.status || 500).json({ error: process.env.NODE_ENV === 'production' ? '服务器错误' : err.message }); }); // 404 处理 app.use((req, res) => { res.status(404).json({ error: '接口不存在' }); }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`服务器运行在 http://localhost:${PORT}`); }); 

7.2 测试脚本

// test-api.js const axios = require('axios'); const API_URL = 'http://localhost:3000/api'; async function testAPI() { console.log('开始测试 API...n'); try { // 1. 注册 console.log('1. 注册用户'); const registerRes = await axios.post(`${API_URL}/register`, { name: '测试用户', email: `test${Date.now()}@example.com`, password: '123456' }); console.log('注册成功:', registerRes.data); // 2. 登录 console.log('n2. 登录'); const loginRes = await axios.post(`${API_URL}/login`, { email: registerRes.data.email || `test${Date.now()}@example.com`, password: '123456' }); console.log('登录成功:', loginRes.data); const token = loginRes.data.token; // 3. 获取个人资料 console.log('n3. 获取个人资料'); const profileRes = await axios.get(`${API_URL}/profile`, { headers: { Authorization: `Bearer ${token}` } }); console.log('个人资料:', profileRes.data); // 4. 更新资料 console.log('n4. 更新资料'); const updateRes = await axios.put(`${API_URL}/profile`, { name: '测试用户-已更新' }, { headers: { Authorization: `Bearer ${token}` } }); console.log('更新成功:', updateRes.data); console.log('n✅ 所有测试通过!'); } catch (err) { console.error('❌ 测试失败:', err.response?.data || err.message); } } testAPI(); 

第八部分:调试技巧

8.1 使用 Chrome DevTools 调试

# 启动 Node.js 应用时开启调试 node --inspect app.js # 或者使用 PM2 pm2 start app.js --node-args="--inspect" 

然后在 Chrome 浏览器中打开 chrome://inspect,点击 “Open dedicated DevTools for Node”。

8.2 使用 VS Code 调试

创建 .vscode/launch.json

{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "启动程序", "skipFiles": ["<node_internals>/**"], "program": "${workspaceFolder}/app.js", "env": { "NODE_ENV": "development" } }, { "type": "node", "request": "attach", "name": "附加到进程", "skipFiles": ["<node_internals>/**"], "port": 9229 } ] } 

8.3 日志调试

// 调试中间件 const debug = require('debug')('app:main'); app.use((req, res, next) => { debug(`${req.method} ${req.url}`, { body: req.body, query: req.query, params: req.params }); next(); }); // 使用 DEBUG 环境变量运行 DEBUG=app:* node app.js 

总结

通过本文的学习,你已经掌握了 Node.js 后端开发的核心技能:

  1. 基础入门:环境搭建、模块系统、HTTP 服务器
  2. Express 框架:路由、中间件、错误处理
  3. 数据库操作:MongoDB 连接、CRUD 操作、数据验证
  4. 常见报错:模块、端口、异步、数据库、CORS 等问题的解决方案
  5. 生产实践:PM2、日志、安全、配置管理
  6. 实战项目:完整的用户管理系统

下一步学习建议

  • 学习 JWT 认证和授权
  • 掌握 Redis 缓存
  • 学习 WebSocket 实时通信
  • 了解微服务架构
  • 学习 Docker 容器化部署

记住,编程最好的学习方式是动手实践。建议你按照文中的示例代码一步步操作,遇到问题时善用 console.log 和调试工具。祝你学习顺利!