Express

技术栈
后端框架
expressnodejsweb框架中间件REST API后端

概览

Express 技术栈概览

Express 是 Node.js 最流行的 极简 Web 框架,由 TJ Holowaychuk 于 2010 年创建。它只提供最核心的路由和中间件机制,其余功能通过插件生态扩展。凭借简单、灵活、学习成本极低的特点,成为 Node.js 后端入门的首选。

解决什么问题

  • 快速搭建 HTTP 服务器:几行代码即可启动 RESTful API
  • 中间件架构:通过 app.use() 链式处理请求,日志/认证/解析等即插即用
  • 毕设快速原型:从零到 API 跑通只需 30 分钟
  • 生态丰富:passport(认证)、morgan(日志)、cors、helmet 等开箱即用

关键特性

  • 极简路由:app.get/post/put/delete 对应 REST 动词
  • 中间件模型:请求按顺序经过中间件管道
  • 模板引擎无关:支持 EJS/Pug/Handlebars
  • 庞大的社区和插件生态

安装

环境准备

  • 操作系统:Windows 10+ / macOS 12+ / Ubuntu 20.04+
  • 运行时版本:Node.js 18 LTS 或 20 LTS
  • 依赖项:npm(随 Node.js 自带)

安装命令

Express 是 npm 包,无需额外安装工具。在项目目录中初始化即可:

# 1. 创建并进入项目目录
mkdir my-express-app &;& cd my-express-app

# 2. 初始化 npm 项目
npm init -y

# 3. 安装 Express
npm install express

# 4. 安装开发依赖(nodemon 热重载)
npm install -D nodemon

# 5. 修改 package.json scripts
# "scripts": { "start": "node index.js", "dev": "nodemon index.js" }

可选常用中间件

npm install cors morgan helmet dotenv

一键生成项目(express-generator)

npx express-generator my-app --view=ejs
cd my-app &;& npm install
npm start

常见安装问题

问题 解决方案
require('express') 找不到 确认在项目目录执行了 npm install express,检查 node_modules/ 是否存在
端口 3000 被占用 修改 app.listen(PORT) 的端口号,或 npx kill-port 3000
nodemon 报权限错误 npx nodemon index.js 或全局安装 npm i -g nodemon
ESM/CJS 混用报错 Express 4.x 默认 CJS(require),若用 "type": "module" 需改为 import express from 'express'

示例

Express RESTful API — 图书管理 CRUD

目标

15 分钟搭建完整的 RESTful API,包含 GET/POST/PUT/DELETE 操作,数据存内存(毕设可替换为数据库)。

完整代码

// app.js
const express = require('express');
const app = express();

app.use(express.json());

// 内存数据库
let books = [
  { id: 1, title: 'Node.js 实战', author: 'Mike Cantelon', year: 2021 },
  { id: 2, title: 'JavaScript 高级程序设计', author: 'Matt Frisbie', year: 2023 },
];
let nextId = 3;

// 中间件:请求日志
app.use((req, res, next) => {
  console.log(`[${new Date().toLocaleTimeString()}] ${req.method} ${req.url}`);
  next();
});

// GET /books — 获取所有图书(支持搜索)
app.get('/books', (req, res) => {
  const { q } = req.query;
  if (q) {
    const filtered = books.filter(b =>
      b.title.includes(q) || b.author.includes(q)
    );
    return res.json({ count: filtered.length, data: filtered });
  }
  res.json({ count: books.length, data: books });
});

// GET /books/:id — 获取单本
app.get('/books/:id', (req, res) => {
  const book = books.find(b => b.id === parseInt(req.params.id));
  if (!book) return res.status(404).json({ error: '图书不存在' });
  res.json(book);
});

// POST /books — 新增图书
app.post('/books', (req, res) => {
  const { title, author, year } = req.body;
  if (!title || !author) {
    return res.status(400).json({ error: '标题和作者为必填' });
  }
  const book = { id: nextId++, title, author, year: year || new Date().getFullYear() };
  books.push(book);
  res.status(201).json(book);
});

// PUT /books/:id — 更新图书
app.put('/books/:id', (req, res) => {
  const book = books.find(b => b.id === parseInt(req.params.id));
  if (!book) return res.status(404).json({ error: '图书不存在' });
  Object.assign(book, req.body);
  res.json(book);
});

// DELETE /books/:id — 删除图书
app.delete('/books/:id', (req, res) => {
  const index = books.findIndex(b => b.id === parseInt(req.params.id));
  if (index === -1) return res.status(404).json({ error: '图书不存在' });
  books.splice(index, 1);
  res.json({ message: '已删除' });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`📚 图书 API: http://localhost:${PORT}/books`));

运行与测试

npm install express
node app.js
# 测试命令(另开终端)
curl http://localhost:3000/books                    # 获取全部
curl http://localhost:3000/books/1                  # 单本
curl -X POST http://localhost:3000/books \
  -H "Content-Type: application/json" \
  -d '{"title":"Go语言编程","author":"许式伟"}'      # 新增
curl -X PUT http://localhost:3000/books/1 \
  -H "Content-Type: application/json" \
  -d '{"year":2024}'                                 # 更新
curl -X DELETE http://localhost:3000/books/1         # 删除
curl "http://localhost:3000/books?q=Node"            # 搜索

预期输出

// GET /books
{"count":2,"data":[{"id":1,"title":"Node.js 实战",...},{"id":2,...}]}
// POST /books → 201 Created
{"id":3,"title":"Go语言编程","author":"许式伟","year":2024}

Express 文件上传 + 静态文件服务

目标

使用 Multer 处理单文件/多文件上传,同时提供静态文件访问,是毕设中「头像上传」「文件提交」的标配方案。

完整代码

// app.js
const express = require('express');
const multer = require('multer');
const path = require('path');
const crypto = require('crypto');

const app = express();

// ───── 配置 Multer ─────
const storage = multer.diskStorage({
  destination: (req, file, cb) => cb(null, 'uploads/'),
  filename: (req, file, cb) => {
    const ext = path.extname(file.originalname);
    const name = crypto.randomBytes(16).toString('hex');
    cb(null, `${name}${ext}`);
  },
});

const fileFilter = (req, file, cb) => {
  const allowed = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
  if (allowed.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error('仅支持 JPG/PNG/GIF/PDF 文件'), false);
  }
};

const upload = multer({ storage, fileFilter, limits: { fileSize: 5 * 1024 * 1024 } });

// ───── 静态文件服务 ─────
app.use('/uploads', express.static('uploads'));
app.use(express.static('public'));

// ───── API 路由 ─────

// 单文件上传(头像)
app.post('/api/upload/avatar', upload.single('avatar'), (req, res) => {
  if (!req.file) return res.status(400).json({ error: '请选择文件' });
  res.json({
    message: '上传成功',
    url: `/uploads/${req.file.filename}`,
    originalName: req.file.originalname,
    size: req.file.size,
  });
});

// 多文件上传(论文附件)
app.post('/api/upload/attachments', upload.array('files', 10), (req, res) => {
  if (!req.files || req.files.length === 0) {
    return res.status(400).json({ error: '请选择文件' });
  }
  const uploaded = req.files.map(f => ({
    url: `/uploads/${f.filename}`,
    name: f.originalname,
    size: f.size,
  }));
  res.json({ message: `成功上传 ${uploaded.length} 个文件`, files: uploaded });
});

// 错误处理
app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    if (err.code === 'LIMIT_FILE_SIZE') {
      return res.status(400).json({ error: '文件大小不能超过 5MB' });
    }
    return res.status(400).json({ error: err.message });
  }
  res.status(500).json({ error: err.message });
});

app.listen(3000, () => console.log('📁 http://localhost:3000'));

配套 HTML 测试页面

<!-- public/index.html -->
<h2>头像上传</h2>
<form id="avatarForm">
  <input type="file" name="avatar" accept="image/*">
  <button type="submit">上传头像</button>
</form>
<img id="preview" src="" style="max-width:200px;display:none;">

<h2>附件上传(多选)</h2>
<form id="filesForm">
  <input type="file" name="files" multiple>
  <button type="submit">上传附件</button>
</form>
<div id="fileList"></div>

<script>
  document.getElementById('avatarForm').addEventListener('submit', async (e) => {
    e.preventDefault();
    const fd = new FormData(e.target);
    const res = await fetch('/api/upload/avatar', { method: 'POST', body: fd });
    const data = await res.json();
    document.getElementById('preview').src = data.url;
    document.getElementById('preview').style.display = 'block';
  });

  document.getElementById('filesForm').addEventListener('submit', async (e) => {
    e.preventDefault();
    const fd = new FormData(e.target);
    const res = await fetch('/api/upload/attachments', { method: 'POST', body: fd });
    const data = await res.json();
    document.getElementById('fileList').innerHTML = data.files
      .map(f => `<p><a href="${f.url}">${f.name}</a> (${(f.size/1024).toFixed(1)}KB)</p>`)
      .join('');
  });
</script>

运行

mkdir uploads public
npm install express multer
node app.js

教程

Express 毕设实战 — 快速搭建后端 API

前言

Express 是 Node.js 最经典的 Web 框架,没有之一。它足够简单,但配合中间件生态能构建企业级应用。毕选用 Express 的最大优势是 :从零到能演示的 API 只需一个晚上。

第一章:中间件机制 — Express 的灵魂

Express 的核心就是 中间件管道。每个请求依次经过所有中间件:

// 中间件1: 日志
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next(); // 不调 next() 请求就卡住了!
});

// 中间件2: 解析 JSON body
app.use(express.json());

// 中间件3: 认证(仅 /api 路由)
app.use('/api', (req, res, next) => {
  if (!req.headers.authorization) {
    return res.status(401).json({ error: '未登录' });
  }
  next();
});

// 最终处理
app.get('/api/data', (req, res) => {
  res.json({ message: '通过认证' });
});

执行顺序:请求 → 日志 → JSON解析 → 认证检查 → 路由处理 → 响应

第二章:错误处理

毕设答辩时如果程序崩溃就尴尬了。Express 的错误处理中间件是最后防线:

// 业务中抛出错误
app.get('/user/:id', async (req, res, next) => {
  try {
    const user = await db.findUser(req.params.id);
    if (!user) {
      const err = new Error('用户不存在');
      err.status = 404;
      throw err;
    }
    res.json(user);
  } catch (err) {
    next(err); // 交给错误处理中间件
  }
});

// 全局错误处理(必须有 4 个参数)
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(err.status || 500).json({
    error: err.message || '服务器内部错误',
  });
});

第三章:文件上传

毕设常见需求:头像上传、论文提交、图片管理。Multer 一行搞定:

const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('file'), (req, res) => {
  // req.file 包含文件信息
  res.json({
    filename: req.file.originalname,
    size: req.file.size,
    url: `/uploads/${req.file.filename}`,
  });
});

第四章:Express 生态必知中间件

中间件 用途 安装
morgan HTTP 请求日志 npm i morgan
cors 跨域资源共享 npm i cors
helmet 安全响应头 npm i helmet
express-rate-limit API 限流 npm i express-rate-limit
compression Gzip 压缩 npm i compression
const morgan = require('morgan');
const cors = require('cors');
const helmet = require('helmet');

app.use(helmet());
app.use(cors());
app.use(morgan('dev'));
app.use(express.json());

思考题

  1. 如果中间件 A 不调用 next(),中间件 B 会被执行吗?
  2. 如何优雅地处理未捕获的 Promise rejection?
  3. Express 和 Koa 的中间件模型有什么区别?(洋葱模型 vs 线性管道)

参考资料

  1. [1] OpenJS Foundation. Express 官方文档. 2024. https://expressjs.com/
  2. [2] Express 中文社区. Express 中文文档. 2023. https://expressjs.com/zh-cn/
  3. [3] David Herron. Node.js Web Development. 2020.
  4. [4] Azat Mardan. Express.js Guide: The Comprehensive Book on Express.js. 2014.