搞懂 express 原理

129 阅读3分钟

一个简单的服务器

先从手写一个简单的服务器入手,用小步走的方式一步步实现完善 express 的架构。

const http = require('http');
const url = require('url');

// 假设这个是路由系统
const Router = [
  {
    path: '*',
    method: '*',
    handler(req, res) {
      res.end(`Cannot ${req.method} ${req.url}`)
    }
  }
]

// 创建应用
function createApplication() {
  return {
    // 应用的 get 方法
    get(url, handler) {
      Router.push({
        path: url,
        method: 'get',
        handler,
      })
    },
    // 服务器监听方法
    listen() {
      const server = http.createServer((req, res) => {
        const { pathname } = url.parse(req.url);

        for(let i = 1; i < Router.length; i++) {
          const { path, method, handler } = Router[i];

          if(pathname === path && method === req.method.toLowerCase()) {
            handler(req, res);
          }
        }
      });

      server.listen(...arguments);
    }
  }
}

分层设计

express.js-主入口

主入口 express.js 写 createApplication 方法并导出。

const Application  = require('./application');

function createApplication() {
  return new Application;
}

module.exports = createApplication;

application.js-创建应用

创建应用的过程由 apllication.js 负责。

application.js 返回一个 Application 类,原型写 app 的方法。

暂时 Application 内部维护 router

const http = require('http');
const url = require('url');

function Application() {
    this._router =  {
        path: '*',
        method: '*',
        handler(req, res) {
          res.end(`Cannot ${req.method} ${req.url}`)
        }
    }
}

Application.prototype.get = function(url, handler) {
  this_router.push({
    path: url,
    method: 'get',
    handler,
  })
}

Application.prototype.listen = function() {
  const server = http.createServer((req, res) => {
    const { pathname } = url.parse(req.url);

    for(let i = 1; i < this._router.length; i++) {
      const { path, method, handler } = this._router[i];

      if(pathname === path && method === req.method.toLowerCase()) {
        return handler(req, res);
      }
      return this._router[0].handler(req, res);
    }
  });

  server.listen(...arguments);
}

module.exports = Application;

应用系统和路由系统分离

层级划分 image.png

各层引用关系

image.png 各层级数据结构

image.png

应用系统

Application 层

const http = require('http');
const url = require('url');

const Router = require('./router');

function Application() {
  this._router = new Router();
}

Application.prototype.get = function(url, ...handlers) {
  // 调用路由系统内部方法处理
  this._router.get(url, handlers);
}

Application.prototype.listen = function() {
  const server = http.createServer((req, res) => {

    const done = () => {
      if(req.method !== 'GET' || req.url !== '/favicon.ico') {
        console.log(`Cannot ${req.method} ${req.url}`);
        res.end(`Cannot ${req.method} ${req.url}`);
      }
    }

    // 路由的事情交给路由自己处理
    this._router.handler(req, res, done);
  });

  server.listen(...arguments);
}

module.exports = Application;

路由系统

Router 层

const url = require('url');

const Route = require('./route');
const Layer = require('./layer');

function Router() {
  this.stack = []; // 存放 layer 层
}

// 建立 Layer 和 Route 的关系
Router.prototype.route = function(path) {
  const route = new Route();

  const layer = new Layer(path, route.dispatch.bind(route));
  layer.route = route;

  // 把layer添加到stack
  this.stack.push(layer);

  return route;
}

Router.prototype.get = function(path, handlers) {
  const route = this.route(path);
  route.get(handlers);
}

Router.prototype.handler = function(req, res, out) {
  let idx = 0;

  const next = () => {
    // 超出递归边界,执行外层传过来的 out(即执行 application 传过来的 done),最终退出
    if(idx >= this.stack.length) return out();

    const { pathname } = url.parse(req.url);
    const layer = this.stack[idx++];

    // 外层 layer 匹配 path
    if(layer.path === pathname) {
      // 处理 layer 层的 route
      layer.handle_request(req, res, next);
    } else {

      // 不匹配就走下一个 layer 层
      next();
    }
  }

  next();
}

module.exports = Router;

Layer 层

function Layer(path, handler) {
  this.path = path;
  this.handler = handler;
}

Layer.prototype.handler = function(req, res, next) {
  this.handler(req, res, next);
}

Layer.prototype.handle_request = function(req, res, next) {
  this.handler(req, res, next)
}

module.exports = Layer;

Route 层

const Layer = require('./layer');

function Route() {
  this.stack = []; // 存放 layer 层
}

Route.prototype.get = function(handlers) {
  handlers.forEach(handler => {
    // 这个 Layer 类接收的第一个参数 path 可以看作是占位,没实际用处,因为内层只根据 method 匹配
    const layer = new Layer('/', handler);
    layer.method = 'get';
  
    this.stack.push(layer);
  });
}

Route.prototype.dispatch = function(req, res, out) {
  let idx = 0;

  const next = () => {
    // 超出递归边界,执行外层传过来的 out(即执行外层的下一个layer),退出内层
    if(idx >= this.stack.length) return out();

    const layer = this.stack[idx++];

    // 内层 layer 匹配 method
    if(layer.method === req.method.toLowerCase()) {
      layer.handle_request(req, res, next);
    } else {
      next();
    }
  }

  next();
}

module.exports = Route;

测试

const express = require('./lib/express');

const app = express();

app.get('/', function(req, res, next) {
  console.log('get / 1-1');
  next();
}, function(req, res, next) {
  console.log('get / 1-2');
  next();
}, function(req, res, next) {
  console.log('get / 1-3');
  // res.end('Hello ');
  next();
},);

app.get('/', function(req, res, next) {
  console.log('get / 2-1');
  res.end('get / 2-1')
})

app.listen(3005);

结果

image.png

总结

  1. Application 是 express 的整个的应用系统,内部放置了一个路由系统

  2. Application 和 Router 分离

  3. express 的路由系统划分了 Router、Layer、Route 三个类,中间件的调用一共分为内外两层判断,外层过滤 path,内层过滤 method

  4. express 原理思路

image.png