基于 Express 的毕业设计实战:从零构建高可用 RESTful API 服务
1. 学生常见痛点:为什么 Demo 永远跑不到线上
做毕业设计时,很多同学把“能跑起来”当成终点,结果代码越写越像“意大利面条”:
- 路由全部堆在
app.js,一屏拉不到底 console.log当日志,重启后全丢- 鉴权靠“前端传个
isAdmin=true”,答辩现场被老师一秒击穿 - 本地
npm start没问题,放到云服务器就 404,路径大小写踩坑 - 端口、密钥、数据库密码全写死在代码里,GitHub 一开源直接“社死”
这些问题总结成一句话:缺少“工程化”思维。毕业设计不是课程作业,它得“可维护、可部署、可演示”。下面就用 Express 给出一套“能毕业、能上线”的最小实战范式。
2. Express 选型:为什么不是 Koa 或 Fastify
轻量 Node 框架三剑客常年被拿来对比,我直接给结论:
| 维度 | Express | Koa | Fastify |
|---|---|---|---|
| 生态 | 最丰富,轮子多 | 次之 | 增长快 |
| 学习曲线 | 最平缓 | 中间 | 需理解 JSON Schema |
| 性能 | 中等 | 中等 | 最高(Schema 优化) |
| 毕业设计场景 | 老师都认识,资料最多 | 需要解释“洋葱模型” | 需要解释“高性能”理由 |
结论:除非你对性能有极致要求,否则 Express 是“最不会出错”的选择——老师懂、学长懂、云厂商模板也懂,节约答辩沟通成本。
3. 核心实现:四步搭出“能上线”的骨架
3.1 模块化路由:按业务拆文件
把路由从app.js拆出来,目录如下:
- routes/
- index.js // 聚合所有子路由
- user.js // 用户相关
- paper.js // 论文相关
关键代码(routes/user.js):
const express = require('express'); const router = express.Router(); const userCtrl = require('../controller/user'); router.post('/register', userCtrl.register); router.post('/login', userCtrl.login); module.exports = router;在routes/index.js统一挂载:
module.exports = (app) => { app.use('/api/users', require('./user')); app.use('/api/papers', require('./paper')); };好处:
- 路由文件只负责“指路”,逻辑下沉到 controller,单一职责。
- 新增模块只需加文件,不碰旧代码,符合开闭原则。
3.2 自定义错误中间件:统一格式、统一日志
没有错误规范的 API,前端收到500就抓瞎。定义一套“业务级别”错误码:
class BusinessError extends Error { constructor(code, msg, status = 400) { super(msg); this.code = code; this.status = status; } }错误中间件(middleware/error_handler.js):
module.exports = (err, req, res, next) => { // 已知业务错误 if (err instanceof BusinessError) { return res.status(err.status).json({ code: err.code, msg: err.message }); } // 未知错误 console.error(err); res.status(500).json({ code: 'INTERNAL_ERROR', msg: '服务器走神了' }); };在app.js最后挂载:
const errorHandler = require('./middleware/error_handler'); app.use(errorHandler);效果:
- 前端拿到
{code: 10001, msg: '手机号已存在'}直接弹窗,无需解析 HTML。 - 未知错误落盘日志,方便定位。
3.3 JWT 令牌验证:无状态登录
毕业项目常见“把用户信息塞 session”——结果服务器一重启全掉线。用 JWT 实现真·无状态:
安装依赖:
npm i jsonwebtoken bcryptjs登录接口(controller/user.js片段):
const jwt = require('jsonwebtoken'); const bcrypt = require('bcryptjs'); const { BusinessError } = require('../utils/error'); exports.login = async (req, res, next) => { const { email, password } = req.body; const user = await User.findOne({ email }); if (!user) return next(new BusinessError('USER_NOT_FOUND', '用户不存在')); const ok = await bcrypt.compare(password, user.passwordHash); if (!ok) return next(new BusinessError('PASSWORD_ERROR', '密码错误')); const token = jwt.sign({ uid: user.id }, process.env.JWT_SECRET, { expiresIn: '7d' }); res.json({ token }); };验证中间件(middleware/auth.js):
const jwt = require('jsonwebtoken'); module.exports = (req, res, next)用最严格格式输出纯 Markdown 正文,不要任何额外说明 [](https://t.csdnimg.cn/iKHO) ---