10-Express实现原理

nobility 发布于 2025-12-26 01-Express 2029 次阅读


Express 实现原理

在 Express 对象中存储一系列请求方法注册的中间件队列,使用 use()get() 等方法向队列中添加中间件对象,中间件对象包括路径和方法两个属性,当请求匹配到中间件中的请求路径前缀时就会执行该中间件方法

Next 方法是一个有名的立即执行函数,当第一次匹配时会立即执行,并将该函数传递给用户中间件的方法为参数,若用户调用 next 方法时会调用我们定义好的 next 方法执行下一个中间件方法

const http = require("http");

/**
 * 校验参数,并返回一个中间件对象
 * @param  {...any} args 路径或中间件回调函数的可变参数
 */
function checkout(...args) {
  const middleware = {};
  if (typeof args[0] === "string") {
    middleware.path = args[0];
    middleware.queue = args.slice(1);
  } else {
    middleware.path = "/";
    middleware.queue = args.slice(0);
  }
  return middleware;
}
/**
 * 为http中的请求响应对象扩展方法
 * @param {Request} req 请求对象
 * @param {Response} res 响应对象
 */
function extendsMethod(req, res) {
  res.send = (data) => {
    res.setHeader("Content-type", "application/json");
    res.end(JSON.stringify(data))
  }
}
/**
 * 根据请求响应对象返回正真要触发的路由列表
 * @param {Request} req 请求对象
 * @param {Response} res 响应对象
 * @param {Routers} routers Express中的路由对象
 */
function getRealRouters(req, res, routers) {
  let curRouters = []; //存储可能要触发路由列表
  let realRouters = []; //要真实触发的路由列表
  curRouters.push(...routers.use);  //use中所有中间件都可能会被执行
  curRouters.push(...routers[req.method.toLowerCase()]);  //根据请求方法获取相应可能执行的中间件
  //由于中间件队列变量是小写的所以要要将请求方法转小写
  curRouters.forEach(item => {
    if (req.url.indexOf(item.path) === 0) { //请求路径与中间件路径前缀匹配时
      //前缀匹配是为了注册前缀中间件可被执行到
      realRouters.push(...item.queue); //会将中间件中的方法加入到要触发的中间件中
    }
  })
  return realRouters;
}
class Express {
  constructor() {
    this.routers = {
      use: [], //存放use方法注册的中间件
      get: [], //存放get方法注册的中间件
    }
  }
  use(...args) {
    const middleware = checkout(...args);
    this.routers.use.push(middleware); //添加到use队列
  }
  get(...args) {
    const middleware = checkout(...args);
    this.routers.get.push(middleware); //添加到get队列
  }
  listen(...args) {
    const server = http.createServer((req, res) => {
      extendsMethod(req, res); //扩展req和res对象方法
      const realRouters = getRealRouters(req, res, this.routers); //获取要执行的中间件队列
      (function next() {
        const middleware = realRouters.shift();
        if (middleware) { //取出一个中间件方法,若不为null
          middleware(req, res, next); //则执行该方法,并将req和res参数传递給该中间件
          //最重要的就是将next方法本身传递给该中间件,若中间件内部调用next方法就又会执行该立即执行函数
        }
      })();//立即执行
    });

    server.listen(...args);
  }
}
module.exports = () => {
  //工厂函数,外部调用该方法即可返回一个Express实例
  return new Express()
}
加油啊!即便没有转生到异世界,也要拿出真本事!!!\(`Δ’)/
最后更新于 2025-12-26