查看: 143|回复: 1

Express 框架从入门到生产实践:路由、中间件与安全配置

[复制链接]
发表于 昨天 22:00 | 显示全部楼层 |阅读模式
Node.js 的 Express 框架是目前最流行的 Web 框架之一,其简洁的路由机制和中间件体系为构建 RESTful API 和 Web 应用提供了坚实基础。本文从零开始,逐步深入,涵盖基础路由、模块化路由、中间件分类与自定义、统一错误处理、文件上传、安全防护以及性能优化等生产级实践,所有代码基于 Express 4.x 版本。
  1. // 第一个 Express 应用
  2. const express = require('express');
  3. const app = express();
  4. const PORT = 3000;
  5. app.get('/', (req, res) => {
  6.   res.send('Hello Express!');
  7. });
  8. app.listen(PORT, () => {
  9.   console.log(`Server running on http://localhost:${PORT}`);
  10. });
复制代码

核心解析:express() 创建应用实例,封装 HTTP 服务器逻辑;app.get(path, handler) 定义 GET 请求路由;res.send() 自动设置 Content-Type。

一、路由:HTTP 方法与参数处理
Express 支持所有标准 HTTP 方法:GET、POST、PUT、DELETE 等。路由参数通过冒号定义,存储在 req.params;查询字符串通过 req.query 获取。
  1. // 基本路由示例
  2. app.get('/users', (req, res) => { res.json({ users: ['Alice', 'Bob'] }); });
  3. app.post('/users', (req, res) => { res.status(201).json({ message: 'User created' }); });
  4. app.put('/users/:id', (req, res) => { res.json({ message: `User ${req.params.id} updated` }); });
  5. app.delete('/users/:id', (req, res) => { res.json({ message: `User ${req.params.id} deleted` }); });
  6. // 路由参数与查询字符串
  7. app.get('/users/:id/posts', (req, res) => {
  8.   const { id } = req.params;
  9.   const { page = 1, limit = 10 } = req.query;
  10.   res.json({ userId: id, page, limit });
  11. });
复制代码

二、路由模块化:express.Router()
当路由增多时,使用 Router 将代码按模块拆分。
  1. // routes/users.js
  2. const express = require('express');
  3. const router = express.Router();
  4. router.get('/', (req, res) => res.json({ users: [] }));
  5. router.get('/:id', (req, res) => res.json({ user: req.params.id }));
  6. module.exports = router;
  7. // app.js 主文件
  8. const userRouter = require('./routes/users');
  9. app.use('/users', userRouter);
复制代码

此外,app.route() 可对同一资源链式定义多个 HTTP 方法,减少重复代码。

三、中间件:Express 的灵魂
中间件函数可访问 req、res 和 next,按执行顺序分为应用级、路由级、错误处理、内置与第三方中间件。

1. 常用内置中间件
express.json() 解析 JSON 请求体,express.urlencoded() 解析 URL 编码请求体,express.static() 托管静态文件。
  1. app.use(express.json());
  2. app.use(express.urlencoded({ extended: false }));
  3. app.use(express.static('public'));
复制代码

2. 自定义应用级中间件
通过 app.use() 定义全局或路径特定的中间件,一定调用 next() 否则请求挂起。
  1. app.use((req, res, next) => {
  2.   console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
  3.   next();
  4. });
复制代码

3. 错误处理中间件
必须有 4 个参数 (err, req, res, next),且位于所有中间件和路由之后。
  1. app.get('/error', (req, res, next) => {
  2.   const error = new Error('Something went wrong!');
  3.   error.status = 500;
  4.   next(error);
  5. });
  6. app.use((err, req, res, next) => {
  7.   const status = err.status || 500;
  8.   const message = err.message || 'Internal Server Error';
  9.   res.status(status).json({ error: message });
  10. });
复制代码

四、统一错误处理:同步与异步错误
Express 4.x 默认不捕获 async/await 中的错误,需手动包装。

定义统一错误类:
  1. class AppError extends Error {
  2.   constructor(message, statusCode) {
  3.     super(message);
  4.     this.statusCode = statusCode;
  5.     this.isOperational = true;
  6.     Error.captureStackTrace(this, this.constructor);
  7.   }
  8. }
复制代码

创建异步包装器:
  1. const asyncWrapper = (fn) => {
  2.   return (req, res, next) => {
  3.     fn(req, res, next).catch(next);
  4.   };
  5. };
复制代码

使用示例:
  1. app.get('/users/:id', asyncWrapper(async (req, res, next) => {
  2.   const user = await User.findById(req.params.id);
  3.   if (!user) throw new AppError('User not found', 404);
  4.   res.json({ user });
  5. }));
复制代码

错误处理中间件区分业务错误与系统错误,可仅在开发环境返回堆栈:
  1. app.use((err, req, res, next) => {
  2.   const { statusCode = 500, message, isOperational } = err;
  3.   res.status(statusCode).json({
  4.     error: isOperational ? message : 'Internal Server Error',
  5.     ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
  6.   });
  7. });
复制代码

五、RESTful API 设计实践
资源导向,HTTP 方法对应 CRUD。以用户管理为例:
  1. let users = [
  2.   { id: 1, name: 'Alice', email: 'alice@example.com' },
  3.   { id: 2, name: 'Bob', email: 'bob@example.com' }
  4. ];
  5. app.get('/users', asyncWrapper(async (req, res) => {
  6.   const { page = 1, limit = 10 } = req.query;
  7.   const start = (page - 1) * limit;
  8.   const end = start + parseInt(limit);
  9.   const paginatedUsers = users.slice(start, end);
  10.   res.json({ data: paginatedUsers, page: parseInt(page), limit: parseInt(limit), total: users.length });
  11. }));
  12. app.post('/users', asyncWrapper(async (req, res, next) => {
  13.   const { name, email } = req.body;
  14.   if (!name || !email) throw new AppError('Name and email are required', 400);
  15.   const newUser = { id: users.length + 1, name, email };
  16.   users.push(newUser);
  17.   res.status(201).json({ data: newUser });
  18. }));
复制代码

六、文件上传:Multer 中间件
Multer 处理 multipart/form-data。
  1. const multer = require('multer');
  2. const storage = multer.diskStorage({
  3.   destination: (req, file, cb) => { cb(null, 'uploads/'); },
  4.   filename: (req, file, cb) => { cb(null, `${Date.now()}-${file.originalname}`); }
  5. });
  6. const fileFilter = (req, file, cb) => {
  7.   if (file.mimetype.startsWith('image/')) cb(null, true);
  8.   else cb(new AppError('Only images are allowed', 400), false);
  9. };
  10. const upload = multer({ storage, fileFilter, limits: { fileSize: 5 * 1024 * 1024 } });
  11. // 单文件上传
  12. app.post('/upload/avatar', upload.single('avatar'), (req, res) => {
  13.   res.json({ message: 'Avatar uploaded', file: req.file });
  14. });
  15. // 多文件上传
  16. app.post('/upload/photos', upload.array('photos', 5), (req, res) => {
  17.   res.json({ message: 'Photos uploaded', files: req.files });
  18. });
复制代码

七、安全最佳实践
1. Helmet:设置安全 HTTP 头
  1. const helmet = require('helmet');
  2. app.use(helmet());
复制代码

2. CORS:跨域资源共享
  1. const cors = require('cors');
  2. // 允许所有来源(开发)
  3. app.use(cors());
  4. // 或限制特定来源(生产)
  5. app.use(cors({ origin: 'https://your-frontend.com', methods: ['GET','POST','PUT','DELETE'] }));
复制代码

3. 速率限制:防止暴力攻击
  1. const rateLimit = require('express-rate-limit');
  2. const globalLimiter = rateLimit({ windowMs: 15*60*1000, max: 100 });
  3. app.use(globalLimiter);
  4. // 登录接口更严格
  5. const loginLimiter = rateLimit({ windowMs: 15*60*1000, max: 5 });
  6. app.post('/login', loginLimiter, (req, res) => res.json({ message: 'Login' }));
复制代码

4. 输入验证:使用 Joi
  1. const Joi = require('joi');
  2. const userSchema = Joi.object({
  3.   name: Joi.string().min(2).max(30).required(),
  4.   email: Joi.string().email().required(),
  5.   age: Joi.number().integer().min(18).max(100)
  6. });
  7. app.post('/users', (req, res, next) => {
  8.   const { error } = userSchema.validate(req.body);
  9.   if (error) throw new AppError(error.details[0].message, 400);
  10.   next();
  11. }, (req, res) => {
  12.   res.json({ message: 'User created' });
  13. });
复制代码

八、性能优化
1. 压缩响应
  1. const compression = require('compression');
  2. app.use(compression());
复制代码

2. 静态文件缓存
  1. app.use(express.static('public', { maxAge: '1d' }));
复制代码

3. 避免同步操作
所有 I/O 应使用异步方式(数据库查询、文件读写等),防止阻塞事件循环。

以上实践涵盖了从基础到生产级的 Express 开发要点,开发者可根据项目需求灵活组合各项中间件与配置。
回复

使用道具 举报

发表于 昨天 22:10 | 显示全部楼层

Re: Express 框架从入门到生产实践:路由、中间件与安全配置

感谢楼主分享这么详细的 Express 实践指南!从基础路由到模块化拆分,再到中间件和统一错误处理,结构清晰,代码也很规范。特别是 `asyncWrapper` 这部分,对于刚接触 Express 的新手来说非常实用,能避免很多异步错误漏捕获的问题。想请教下,在生产环境中处理文件上传时,除了 `multer` 中间件,还有没有推荐的安全检查或配置技巧?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-13 01:04 , Processed in 0.037093 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部