Skip to content

2)手写中间件(Express)

总结

  1. 中间件的格式:(req, res, next)(err, req, res, next)
    • next 是实现链式调用的关键。
    • Express 内部通过一个 迭代器机制 逐个执行中间件,next 通过递归调用实现了这一过程。
  2. 使用方式:
    • 通过 app.use() 全局挂载。
    • 通过特定路由挂载。
  3. 中间件执行顺序:从上到下依次执行。
  4. 错误处理中间件通过 next(err) 实现跳过普通中间件,直接处理错误逻辑。

手写 Express 中间件的步骤

  1. 定义一个中间件函数

    • 中间件是一个接受 (req, res, next) 参数的函数。
    • reqres 是 Express 提供的请求和响应对象,next 是调用链中下一个中间件的控制函数。
  2. 在应用中使用中间件

    • 通过 app.use() 全局挂载。
    • 通过特定路由挂载。
  3. 实现具体功能

    • 中间件可用于日志记录、身份验证、请求解析等任务。

完整代码示例

1. 创建一个简单的日志中间件

javascript
// Import the Express library
import express from "express";

const app = express();

// Custom middleware for logging requests
const requestLogger = (req, res, next) => {
  const { method, url } = req;
  const timestamp = new Date().toISOString();
  console.log(`[${timestamp}] ${method} ${url}`);
  next(); // Proceed to the next middleware or route handler
};

// Use the middleware globally
app.use(requestLogger);

// Route example
app.get("/", (req, res) => {
  res.send("Hello, world!");
});

// Another route
app.get("/about", (req, res) => {
  res.send("About page");
});

// Start the server
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

2. 中间件的扩展示例

身份验证中间件

javascript
// Middleware for authentication
const authenticate = (req, res, next) => {
  const token = req.headers["authorization"];

  if (!token) {
    return res.status(401).json({ message: "Unauthorized" });
  }

  // Simulate token verification (replace with real logic)
  if (token === "valid-token") {
    next(); // Proceed to the next middleware or route
  } else {
    res.status(403).json({ message: "Forbidden" });
  }
};

// Apply to a specific route
app.get("/protected", authenticate, (req, res) => {
  res.send("This is a protected route.");
});

3. 错误处理中间件

javascript
// Error handling middleware
const errorHandler = (err, req, res, next) => {
  console.error(err.stack); // Log the error stack
  res
    .status(500)
    .json({ message: "Something went wrong!", error: err.message });
};

// Example of a route with an error
app.get("/error", (req, res) => {
  throw new Error("This is a simulated error");
});

// Use the error handling middleware (should be the last middleware)
app.use(errorHandler);

中间件的核心点

  1. next() 的作用

    • next() 是调用链的核心。如果不调用,后续中间件和路由将无法执行。
  2. 错误处理中间件的特殊性

    • 必须定义 err 参数,形式为 (err, req, res, next)
  3. 挂载顺序

    • 中间件的执行顺序与注册顺序一致,顺序错误可能导致意外行为。

运行结果

  1. 启动服务器:node app.js

  2. 请求 //about 等路由:

    • 日志中间件会打印类似:
      [2024-12-25T08:00:00.000Z] GET /
  3. 请求 /protected 路由:

    • 如果 Authorization 头不为 valid-token,返回:
      json
      { "message": "Unauthorized" }
  4. 请求 /error

    • 返回错误信息:
      json
      {
        "message": "Something went wrong!",
        "error": "This is a simulated error"
      }

Express 迭代器核心代码

以下是一个简化的 Express 核心实现。

js
class Express {
  constructor() {
    // 默认的中间件数组
    this.middlewares = [];
  }

  use(middleware) {
    // 插入中间件到数组中
    this.middlewares.push(middleware);
  }

  handleRequest(req, res) {
    // 默认的中间件索引
    let index = 0;

    const next = (err) => {
      // 如果超过了中间件数组长度,结束
      if (index >= this.middlewares.length) return;
      // 当前中间件
      const middleware = this.middlewares[index++];

      if (err) {
        // 如果是错误处理中间件,调用它
        if (middleware.length === 4) {
          return middleware(err, req, res, next);
        } else {
          // 跳过普通中间件
          return next(err);
        }
      }

      if (middleware.length < 4) {
        return middleware(req, res, next); // 调用普通中间件
      }

      next(); // 跳过错误处理中间件
    };

    next(); // 开始执行第一个中间件
  }
}

// 使用
const app = new Express();

app.use((req, res, next) => {
  console.log("Middleware 1");
  next();
});

app.use((req, res, next) => {
  console.log("Middleware 2");
  res.end("Response from Middleware 2");
});

// 模拟请求
app.handleRequest({}, { end: console.log });