Node.js 的 Express 框架是目前最流行的 Web 框架之一,其简洁的路由机制和中间件体系为构建 RESTful API 和 Web 应用提供了坚实基础。本文从零开始,逐步深入,涵盖基础路由、模块化路由、中间件分类与自定义、统一错误处理、文件上传、安全防护以及性能优化等生产级实践,所有代码基于 Express 4.x 版本。
- // 第一个 Express 应用
- const express = require('express');
- const app = express();
- const PORT = 3000;
- app.get('/', (req, res) => {
- res.send('Hello Express!');
- });
- app.listen(PORT, () => {
- console.log(`Server running on http://localhost:${PORT}`);
- });
复制代码
核心解析:express() 创建应用实例,封装 HTTP 服务器逻辑;app.get(path, handler) 定义 GET 请求路由;res.send() 自动设置 Content-Type。
一、路由:HTTP 方法与参数处理
Express 支持所有标准 HTTP 方法:GET、POST、PUT、DELETE 等。路由参数通过冒号定义,存储在 req.params;查询字符串通过 req.query 获取。
- // 基本路由示例
- app.get('/users', (req, res) => { res.json({ users: ['Alice', 'Bob'] }); });
- app.post('/users', (req, res) => { res.status(201).json({ message: 'User created' }); });
- app.put('/users/:id', (req, res) => { res.json({ message: `User ${req.params.id} updated` }); });
- app.delete('/users/:id', (req, res) => { res.json({ message: `User ${req.params.id} deleted` }); });
- // 路由参数与查询字符串
- app.get('/users/:id/posts', (req, res) => {
- const { id } = req.params;
- const { page = 1, limit = 10 } = req.query;
- res.json({ userId: id, page, limit });
- });
复制代码
二、路由模块化:express.Router()
当路由增多时,使用 Router 将代码按模块拆分。
- // routes/users.js
- const express = require('express');
- const router = express.Router();
- router.get('/', (req, res) => res.json({ users: [] }));
- router.get('/:id', (req, res) => res.json({ user: req.params.id }));
- module.exports = router;
- // app.js 主文件
- const userRouter = require('./routes/users');
- app.use('/users', userRouter);
复制代码
此外,app.route() 可对同一资源链式定义多个 HTTP 方法,减少重复代码。
三、中间件:Express 的灵魂
中间件函数可访问 req、res 和 next,按执行顺序分为应用级、路由级、错误处理、内置与第三方中间件。
1. 常用内置中间件
express.json() 解析 JSON 请求体,express.urlencoded() 解析 URL 编码请求体,express.static() 托管静态文件。
- app.use(express.json());
- app.use(express.urlencoded({ extended: false }));
- app.use(express.static('public'));
复制代码
2. 自定义应用级中间件
通过 app.use() 定义全局或路径特定的中间件,一定调用 next() 否则请求挂起。
- app.use((req, res, next) => {
- console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
- next();
- });
复制代码
3. 错误处理中间件
必须有 4 个参数 (err, req, res, next),且位于所有中间件和路由之后。
- app.get('/error', (req, res, next) => {
- const error = new Error('Something went wrong!');
- error.status = 500;
- next(error);
- });
- app.use((err, req, res, next) => {
- const status = err.status || 500;
- const message = err.message || 'Internal Server Error';
- res.status(status).json({ error: message });
- });
复制代码
四、统一错误处理:同步与异步错误
Express 4.x 默认不捕获 async/await 中的错误,需手动包装。
定义统一错误类:- class AppError extends Error {
- constructor(message, statusCode) {
- super(message);
- this.statusCode = statusCode;
- this.isOperational = true;
- Error.captureStackTrace(this, this.constructor);
- }
- }
复制代码
创建异步包装器:- const asyncWrapper = (fn) => {
- return (req, res, next) => {
- fn(req, res, next).catch(next);
- };
- };
复制代码
使用示例:- app.get('/users/:id', asyncWrapper(async (req, res, next) => {
- const user = await User.findById(req.params.id);
- if (!user) throw new AppError('User not found', 404);
- res.json({ user });
- }));
复制代码
错误处理中间件区分业务错误与系统错误,可仅在开发环境返回堆栈:- app.use((err, req, res, next) => {
- const { statusCode = 500, message, isOperational } = err;
- res.status(statusCode).json({
- error: isOperational ? message : 'Internal Server Error',
- ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
- });
- });
复制代码
五、RESTful API 设计实践
资源导向,HTTP 方法对应 CRUD。以用户管理为例:
- let users = [
- { id: 1, name: 'Alice', email: 'alice@example.com' },
- { id: 2, name: 'Bob', email: 'bob@example.com' }
- ];
- app.get('/users', asyncWrapper(async (req, res) => {
- const { page = 1, limit = 10 } = req.query;
- const start = (page - 1) * limit;
- const end = start + parseInt(limit);
- const paginatedUsers = users.slice(start, end);
- res.json({ data: paginatedUsers, page: parseInt(page), limit: parseInt(limit), total: users.length });
- }));
- app.post('/users', asyncWrapper(async (req, res, next) => {
- const { name, email } = req.body;
- if (!name || !email) throw new AppError('Name and email are required', 400);
- const newUser = { id: users.length + 1, name, email };
- users.push(newUser);
- res.status(201).json({ data: newUser });
- }));
复制代码
六、文件上传:Multer 中间件
Multer 处理 multipart/form-data。
- const multer = require('multer');
- const storage = multer.diskStorage({
- destination: (req, file, cb) => { cb(null, 'uploads/'); },
- filename: (req, file, cb) => { cb(null, `${Date.now()}-${file.originalname}`); }
- });
- const fileFilter = (req, file, cb) => {
- if (file.mimetype.startsWith('image/')) cb(null, true);
- else cb(new AppError('Only images are allowed', 400), false);
- };
- const upload = multer({ storage, fileFilter, limits: { fileSize: 5 * 1024 * 1024 } });
- // 单文件上传
- app.post('/upload/avatar', upload.single('avatar'), (req, res) => {
- res.json({ message: 'Avatar uploaded', file: req.file });
- });
- // 多文件上传
- app.post('/upload/photos', upload.array('photos', 5), (req, res) => {
- res.json({ message: 'Photos uploaded', files: req.files });
- });
复制代码
七、安全最佳实践
1. Helmet:设置安全 HTTP 头- const helmet = require('helmet');
- app.use(helmet());
复制代码
2. CORS:跨域资源共享- const cors = require('cors');
- // 允许所有来源(开发)
- app.use(cors());
- // 或限制特定来源(生产)
- app.use(cors({ origin: 'https://your-frontend.com', methods: ['GET','POST','PUT','DELETE'] }));
复制代码
3. 速率限制:防止暴力攻击- const rateLimit = require('express-rate-limit');
- const globalLimiter = rateLimit({ windowMs: 15*60*1000, max: 100 });
- app.use(globalLimiter);
- // 登录接口更严格
- const loginLimiter = rateLimit({ windowMs: 15*60*1000, max: 5 });
- app.post('/login', loginLimiter, (req, res) => res.json({ message: 'Login' }));
复制代码
4. 输入验证:使用 Joi- const Joi = require('joi');
- const userSchema = Joi.object({
- name: Joi.string().min(2).max(30).required(),
- email: Joi.string().email().required(),
- age: Joi.number().integer().min(18).max(100)
- });
- app.post('/users', (req, res, next) => {
- const { error } = userSchema.validate(req.body);
- if (error) throw new AppError(error.details[0].message, 400);
- next();
- }, (req, res) => {
- res.json({ message: 'User created' });
- });
复制代码
八、性能优化
1. 压缩响应- const compression = require('compression');
- app.use(compression());
复制代码
2. 静态文件缓存- app.use(express.static('public', { maxAge: '1d' }));
复制代码
3. 避免同步操作
所有 I/O 应使用异步方式(数据库查询、文件读写等),防止阻塞事件循环。
以上实践涵盖了从基础到生产级的 Express 开发要点,开发者可根据项目需求灵活组合各项中间件与配置。 |