DrillLab
第 89 / 105 道89 / 105 · #314

请求 - 响应周期是怎样的

Explain the request & response cycle

先自己答,再往下看Answer it yourself first

一句话:客户端发请求 → 经过一串中间件 → 匹配到路由 → 处理逻辑(查库等)→发出一个响应→ 结束。

Express 里具体经过什么:

  1. 解析——express.json()把请求体解析成 req.body不加这个中间件req.body 就是undefined, 这是最常见的新手问题)
  2. 通用中间件—— CORS、日志、关联 id、限流
  3. 认证 / 鉴权—— 验 token,把用户挂到req.user
  4. 路由匹配—— 按注册顺序找第一个匹配的 method + path
  5. 校验 → 业务逻辑 → 查库
  6. 响应——res.status(200).json(...)
  7. 兜底—— 404 处理 + 错误处理中间件

三个必须知道的规则:

  • 中间件按注册顺序执行, 必须调 next()才会往下走 —— 忘了调请求就永远挂着, 客户端一直转圈。
  • 一个请求只能响应一次。重复 res.send会报Cannot set headers after they are sent所以调用响应之后一定要return
  • 错误处理中间件有四个参数(err, req, res, next)——少一个参数 Express 就认不出它是错误处理器, 这个坑很经典。

会追问:「异步函数里抛的错误 Express 会自动捕获吗?」——Express 4 里不会, 会变成未处理的 rejection,请求挂死。 要么自己 try/catchnext(err), 要么包一层 asyncHandler。Express 5 才支持自动转发。

In one line: the client sends a request, it walks through a chain of middleware, a route matches, the handler does its work (hits the database and so on), one response goes out, done.

What it actually passes through in Express:

  1. Parsingexpress.json() turns the body into req.body (without that middleware req.body is undefined, which is the single most common beginner bug)
  2. Generic middleware — CORS, logging, a correlation id, rate limiting
  3. Authentication and authorisation — verify the token, hang the user off req.user
  4. Route matching — the first registered method + path that matches wins
  5. Validation, then business logic, then the database
  6. Response res.status(200).json(...)
  7. Catch-alls — the 404 handler and the error-handling middleware

Three rules you have to know:

  • Middleware runs in registration order and you must call next() for the chain to continue. Forget it and the request hangs forever while the client spins.
  • One request gets exactly one response. A second res.send throws Cannot set headers after they are sent. So always return after you respond.
  • Error middleware takes four arguments, (err, req, res, next) drop one and Express no longer recognises it as an error handler. A classic trap.

Follow-up: “Does Express catch errors thrown inside an async function?” — not in Express 4. It becomes an unhandled rejection and the request hangs. Either try/catch yourself and call next(err), or wrap the handler in an asyncHandler. Express 5 forwards them for you.

JavaScript一个请求经过的全部环节示意Illustrative
1app.use(express.json()); // 1 解析 body
2app.use(cors()); // 2 通用
3app.use(correlationId); // 关联 id,方便串联日志
4app.use(auth); // 3 认证
5
6app.get("/orders/:id", async (req, res, next) => {
7 try {
8 const order = await db.find(req.params.id);
9 if (!order) return res.status(404).json({ error: "not found" }); // 记得 return
10 res.json(order);
11 } catch (e) {
12 next(e); // Express 4 不会自动接异步错误
13 }
14});
15
16app.use((req, res) => res.status(404).json({ error: "no route" }));
17
18// 错误处理中间件:必须是四个参数,少一个就不生效
19app.use((err, req, res, next) => {
20 console.error(err);
21 res.status(500).json({ error: "internal" });
22});
1app.use(express.json()); // 1 parse the body
2app.use(cors()); // 2 general
3app.use(correlationId); // a correlation id, so logs can be tied together
4app.use(auth); // 3 authentication
5
6app.get("/orders/:id", async (req, res, next) => {
7 try {
8 const order = await db.find(req.params.id);
9 if (!order) return res.status(404).json({ error: "not found" }); // do not forget return
10 res.json(order);
11 } catch (e) {
12 next(e); // Express 4 does not catch async errors for you
13 }
14});
15
16app.use((req, res) => res.status(404).json({ error: "no route" }));
17
18// The error-handling middleware must take four arguments; with three it never runs
19app.use((err, req, res, next) => {
20 console.error(err);
21 res.status(500).json({ error: "internal" });
22});