代码地址:https://github.com/changeclass/Node_Blog_express.git

# 原理介绍

  • app.use 用来注册中间件
  • 遇到 HTTP 请求,根据 path 和 method 判断触发哪些
  • 实现 next 机制,即上一个通过 next 触发下一个

# 代码实现

  1. 实例化一个类,用于封装 getpostuse 方法

    const http = require("http");
    const slice = Array.prototype.slice;
    class LikeExpress {
        constructor() {
            // 存放中间件的列表
            this.routes = {
                all: [], // app.use(...)
                get: [], // app.get(...)
                post: [], // app.post(...)
            };
        }
    }
  2. 在类的内部实现 use 等方法

    usepostget 都有共同的注册逻辑,因此再将其抽离一层。

    class LikeExpress {
        register(path) {
            const info = {};
            if (typeof path === "string") {
                // 第一个参数是路由时
                info.path = path;
                // 第二个参数开始,转为数据存入 stack
                info.stack = slice.call(arguments, 1);
            } else {
                // 第一个参数是函数
                info.path = "/";
                // 第一个参数开始,转为数据存入 stack
                info.stack = slice.call(arguments, 0);
            }
            return info;
        }
        use() {
            const info = this.register.apply(this, arguments);
            this.routes.all.push(info);
        }
        get() {
            const info = this.register.apply(this, arguments);
            this.routes.get.push(info);
        }
        post() {
            const info = this.register.apply(this, arguments);
            this.routes.post.push(info);
        }
    }
  3. 监听路由

    listen(...args) {
        const server = http.createServer(this.callback());
        server.listen(...args);
      }

    创建 server 时传入回调函数,用于处理路由,即匹配符合规则的路由。匹配路由后即执行 next ( handle ) 方法

    callback() {
        return (req, res) => {
            // 定义 res.json 方法
            res.json = (data) => {
                res.setHeader("Content-type", "application/json");
                res.end(JSON.stringify(data));
            };
            const url = req.url;
            const method = req.method.toLowerCase();
            const resultList = this.match(method, url);
            // 调用下一个 函数
            this.handle(req, res, resultList);
        };
    }
    • match 方法

      具体匹配的逻辑。

      // 匹配符合规则的中间件
      match(method, url) {
          let stack = [];
          if (url === "/favicon.ico") {
              return stack;
          }
          // 获取 routes
          let curRoutes = [];
          // 通过 app.use 注册中间件
          curRoutes = curRoutes.concat(this.routes.all);
          // 通过请求方法注册中间件
          curRoutes = curRoutes.concat(this.routes[method]);
          // 判断当前 url 是否符合根路径
          curRoutes.forEach((routeInfo) => {
              /**
             *  url==='/api/get-cookie' 且 routeInfo.path = '/'
             *  url==='/api/get-cookie' 且 routeInfo.path = '/api'
             *  url==='/api/get-cookie' 且 routeInfo.path = '/api/get-cookie'
             */
              if (url.indexOf(routeInfo.path) === 0) {
                  stack = stack.concat(routeInfo.stack);
              }
          });
          return stack;
      }
    • handle 方法

      // 核心的 next 机制
      handle(req, res, stack) {
          const next = () => {
              // 拿到第一个匹配的中间件
              const middleware = stack.shift();
              if (middleware) {
                  // 执行中间件函数
                  middleware(req, res, next);
              }
          };
          next();
      }
  4. 测试代码

    const express = require("./like-express");
    const app = express();
    console.log(app);
    app.use((req, res, next) => {
      console.log("请求开始", req.method);
      next();
    });
    app.use((req, res, next) => {
      // 假设处理 Cookie
      req.cookie = {
        userId: "123",
      };
      next();
    });
    app.use((req, res, next) => {
      // 假设处理 post data
      // 异步
      setTimeout(() => {
        req.body = {
          a: 100,
          b: 200,
        };
        next();
      });
    });
    app.use("/api", (req, res, next) => {
      console.log("处理/api 路由");
      next();
    });
    app.get("/api", (req, res, next) => {
      console.log("处理/api 路由");
      next();
    });
    app.get("/api/get-cookie", (req, res, next) => {
      console.log("get /api/get-cookie");
      res.json({
        errno: 0,
        data: req.cookie,
      });
    });
    app.listen(3000, () => {
      console.log("server is running on http://127.0.0.1:3000");
    });

    image-20201022103707390

# 完整代码

const http = require("http");
const slice = Array.prototype.slice;
class LikeExpress {
  constructor() {
    // 存放中间件的列表
    this.routes = {
      all: [], // app.use(...)
      get: [], // app.get(...)
      post: [], // app.post(...)
    };
  }
  register(path) {
    const info = {};
    if (typeof path === "string") {
      // 第一个参数是路由时
      info.path = path;
      // 第二个参数开始,转为数据存入 stack
      info.stack = slice.call(arguments, 1);
    } else {
      // 第一个参数是函数
      info.path = "/";
      // 第一个参数开始,转为数据存入 stack
      info.stack = slice.call(arguments, 0);
    }
    return info;
  }
  use() {
    const info = this.register.apply(this, arguments);
    this.routes.all.push(info);
  }
  get() {
    const info = this.register.apply(this, arguments);
    this.routes.get.push(info);
  }
  post() {
    const info = this.register.apply(this, arguments);
    this.routes.post.push(info);
  }
  // 匹配符合规则的中间件
  match(method, url) {
    let stack = [];
    if (url === "/favicon.ico") {
      return stack;
    }
    // 获取 routes
    let curRoutes = [];
    // 通过 app.use 注册中间件
    curRoutes = curRoutes.concat(this.routes.all);
    // 通过请求方法注册中间件
    curRoutes = curRoutes.concat(this.routes[method]);
    // 判断当前 url 是否符合根路径
    curRoutes.forEach((routeInfo) => {
      /**
       *  url==='/api/get-cookie' 且 routeInfo.path = '/'
       *  url==='/api/get-cookie' 且 routeInfo.path = '/api'
       *  url==='/api/get-cookie' 且 routeInfo.path = '/api/get-cookie'
       */
      if (url.indexOf(routeInfo.path) === 0) {
        stack = stack.concat(routeInfo.stack);
      }
    });
    return stack;
  }
  // 核心的 next 机制
  handle(req, res, stack) {
    const next = () => {
      // 拿到第一个匹配的中间件
      const middleware = stack.shift();
      if (middleware) {
        // 执行中间件函数
        middleware(req, res, next);
      }
    };
    next();
  }
  callback() {
    return (req, res) => {
      // 定义 res.json 方法
      res.json = (data) => {
        res.setHeader("Content-type", "application/json");
        res.end(JSON.stringify(data));
      };
      const url = req.url;
      const method = req.method.toLowerCase();
      const resultList = this.match(method, url);
      // 调用下一个 函数
      this.handle(req, res, resultList);
    };
  }
  listen(...args) {
    const server = http.createServer(this.callback());
    server.listen(...args);
  }
}
module.exports = () => {
  return new LikeExpress();
};
更新于

请我喝[茶]~( ̄▽ ̄)~*

Dreamy.TZK 微信支付

微信支付

Dreamy.TZK 支付宝

支付宝