Skip to content

第 5 章:HTTP 服务与中间件 ​

原生 http 手写路由太繁琐,实战都用框架。这一章讲清 Express 的使用和它最核心的「中间件洋葱模型」,顺带对比 Koa。


5.1 从原生 http 到 Express ​

原生 http 处理路由要手动解析 URL,代码又臭又长:

javascript
const http = require('http');
http.createServer((req, res) => {
  if (req.url === '/user' && req.method === 'GET') {
    res.end('用户列表');
  } else if (req.url === '/user' && req.method === 'POST') {
    res.end('创建用户');
  } else {
    res.writeHead(404); res.end('Not Found');
  }
}).listen(3000);

Express 用「路由 + 中间件」把这件事变优雅:

javascript
import express from 'express';
const app = express();

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

app.get('/user', (req, res) => {      // 路由:GET /user
  res.json({ list: [] });
});
app.post('/user', (req, res) => {     // 路由:POST /user
  res.status(201).json({ id: 1 });
});

app.listen(3000);

5.2 什么是中间件 ​

中间件是一个函数,签名 (req, res, next)。它在请求到达路由之前/之后执行,可以做日志、鉴权、解析 body、错误处理等通用逻辑:

javascript
// 日志中间件
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();            // 关键:调用 next() 才会继续往后走
});

// 鉴权中间件
app.use((req, res, next) => {
  if (!req.headers.authorization) {
    return res.status(401).json({ error: '未授权' });
  }
  next();
});

核心规则:中间件里要么调用 next() 放行,要么 res.xxx() 直接返回响应。忘了 next() 请求就会「卡死」。


5.3 中间件洋葱模型(核心) ​

多个中间件的执行顺序,是理解 Express 的钥匙。请求像穿过一颗洋葱,先进后出:

请求 ──► ┌─────────────────────────────┐
         │  中间件1(进入,before)       │
         │   ┌─────────────────────┐   │
         │   │ 中间件2(进入)        │   │
         │   │   ┌─────────────┐   │   │
         │   │   │  路由处理     │   │   │
         │   │   └─────────────┘   │   │
         │   │ 中间件2(退出,after)│   │
         │   └─────────────────────┘   │
         │  中间件1(退出,after)       │
响应 ◄── └─────────────────────────────┘

用代码验证执行顺序:

javascript
app.use((req, res, next) => {
  console.log('A 进入');
  next();
  console.log('A 退出');
});
app.use((req, res, next) => {
  console.log('B 进入');
  next();
  console.log('B 退出');
});
app.get('/', (req, res) => {
  console.log('路由处理');
  res.end('ok');
});

// 输出顺序:A 进入 → B 进入 → 路由处理 → B 退出 → A 退出

为什么是「洋葱」:next() 之前的代码是「进入」阶段,next() 之后的代码是「退出」阶段。这让中间件能在请求前后各做一半事——比如记录耗时、压缩响应。


5.4 Express vs Koa ​

两者都是主流框架,选型参考:

维度ExpressKoa
中间件回调 (req,res,next)async/await,更现代
体积较大,功能全极简核心,靠插件
生态最庞大、资料最多较新,由社区扩展
错误处理手动 try/catchasync 天然传播到 try/catch
上手简单直接需要理解洋葱模型

建议:新手/求稳用 Express(生态最全);追求现代 async 风格、想更灵活用 Koa。原理相通,会一个另一个很快上手。


5.5 一个完整的 REST API 示例 ​

把路由、中间件、错误处理串起来:

javascript
import express from 'express';
const app = express();
app.use(express.json());

// 简单内存数据
const users = [];

// 日志中间件(进入/退出各做一半)
app.use((req, res, next) => {
  const start = Date.now();
  next();
  console.log(`${req.method} ${req.url} 耗时 ${Date.now() - start}ms`);
});

// 路由
app.get('/users', (req, res) => res.json(users));
app.post('/users', (req, res) => {
  users.push(req.body);
  res.status(201).json(req.body);
});

// 兜底错误处理中间件(必须 4 个参数)
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: '服务器错误' });
});

app.listen(3000);

5.6 本章小结 ​

要点说明
中间件(req, res, next),要么 next() 放行,要么直接响应
洋葱模型请求先进后出,next() 前后各执行一半
Express生态最全、上手简单,新手首选
Koaasync 风格更现代,核心更极简

下一章预告 ​

Web 服务会写了,现在进入 AI 时代的主角——用 Node 对接大模型 API,实现 SSE 流式输出,把上一章的 HTTP 和上一章的 stream 串起来。

👉 第 6 章:Node.js 对接大模型

📖本文阅读--次|📊全站访问--次|👥访客--人