node平台express框架源码分析-1

4 篇文章 0 订阅

首先给一个官网express的例子,express的版本是4.14.0

var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

下面将分析上述例子中出现的几个API:

var app = express();
打开node_modules目录找到依赖的源码文件:./express/lib/express.js.该文件导出函数createApplication.

express()即调用了createApplication()函数,app变量是createApplication函数的返回值。

下面是createApplication函数的构造函数:

function createApplication() {
  //创建app对象
  var app = function(req, res, next) {
    app.handle(req, res, next);
  };
  //继承node的事件对象
  mixin(app, EventEmitter.prototype, false);
  //继承./application对象
  mixin(app, proto, false);
  //app.request和response继承node原生的request和response对象
  app.request = { __proto__: req, app: app };
  app.response = { __proto__: res, app: app };
  //初始化app对象
  app.init();
  return app;
}
app.init()方法调用的是继承自./application.js的方法。

下面是application.js中的init方法:

app.init = function init() {
  this.cache = {};
  this.engines = {};
  this.settings = {};

  this.defaultConfiguration();
};
下面是app.defaultConfiguration的源码分析:

app.defaultConfiguration = function defaultConfiguration() {
  //初始化app的默认配置
  ......
  ......

  //监听mount事件,当我们向express中添加中间件的时候会触发mount事件,
  //这里会将每个中间件的request对象,response对象,engines对象,settings对象通过__proto__形成原型连,
  //最顶层的request和response对象是Node原生的request和response对象,在createApplication中定义

  this.on('mount', function onmount(parent) {
    // inherit trust proxy
    if (this.settings[trustProxyDefaultSymbol] === true && typeof parent.settings['trust proxy fn'] === 'function') {
      delete this.settings['trust proxy'];
      delete this.settings['trust proxy fn'];
    }

    // inherit protos
    this.request.__proto__ = parent.request;
    this.response.__proto__ = parent.response;
    this.engines.__proto__ = parent.engines;
    this.settings.__proto__ = parent.settings;
  });

  ......
  ......
};
express()分析结束。

app.get('/', function (req, res) {
  res.send('Hello World!');
});
该api的作用是创建到"/"的get请求的路由处理器,app.get/post/head等方法在application文件中的下述代码中定义:

methods.forEach(function(method) {
  app[method] = function(path) {
    if (method === 'get' && arguments.length === 1) {
      // app.get(setting)
      return this.set(path);
    }

    //给app对象绑定一个路由管理器Router
    this.lazyrouter();

    //使用路由管理器给指定的path创建一个路由对象处理对象
    var route = this._router.route(path);
    //调用路由处理对象的相应方法
    route[method].apply(route, slice.call(arguments, 1));
    return this;
  };
});
下面分析下这行代码的实现:
 var route = this._router.route(path);
在./router/index.js文件中定义的route方法:

proto.route = function route(path) {
  
  //创建并初始化route对象
  var route = new Route(path);

  //创建一个layer对象,并放入栈中,
  //在处理业务的时候会根据path的匹配规则,来匹配对应的route,
  //如果是使用use接口(比如添加中间件的时候),是不指定route的,
  var layer = new Layer(path, {
    sensitive: this.caseSensitive,
    strict: this.strict,
    end: true
  }, route.dispatch.bind(route));

  layer.route = route;

  this.stack.push(layer);
  console.log("app.get(path,cb) return route:" + JSON.stringify(route));
  return route;
};
 route[method].apply(route, slice.call(arguments, 1));
在./router/route.js中定义的对应的method方法:

methods.forEach(function(method) {
  Route.prototype[method] = function() {
    var handles = flatten(slice.call(arguments));
    for (var i = 0; i < handles.length; i++) {
      var handle = handles[i];

      if (typeof handle !== 'function') {
        var type = toString.call(handle);
        var msg = 'Route.' + method + '() requires callback functions but got a ' + type;
        throw new Error(msg);
      }

      debug('%s %s', method, this.path);

      var layer = Layer('/', {}, handle);
      layer.method = method;

      this.methods[method] = true;
      this.stack.push(layer);
      console.log("app.get(path,cb) cb values:" + handle + ";this.path is " + this.path + ";layer:" + JSON.stringify(layer));
      console.log("app.get(path,cb) return route :" + JSON.stringify(this));
    }

    return this;
  };
});
和route方法类似,区别是上下文变了,而且在layer中指定的回调函数是用户定义的回调。

app.get方法分析完毕,总结下 该方法主要完成了两件事:

  1. 根据path创建route对象,根据用户定义的回调事件创建layer对象,将layer对象放入route调用栈中
  2. 根据path创建layer对象,将创建的route对象绑定到layer上,将layer对象放入router的调用栈中
根据上述分析可见express将用户的请求处理分为两个阶段,

第一阶段以router为核心,将用户的请求在router的stack中做循环预处理(中间件处理,根据路由匹配规则)

第二阶段以route为核心,将用户的请求在route的stack中做循环处理(以method为匹配规则)

两阶段的联系是通过./router/route.js中的dispatch方法关联的。

在本节的最后一起来看下app.listen的实现(application.js中):

app.listen = function listen() {
  var server = http.createServer(this);
  return server.listen.apply(server, arguments);
};
其中的this指的是app对象,在前面express()中已经分析过app的原型了,其中app的构造函数就是:

 var app = function(req, res, next) {
    app.handle(req, res, next);
  };
所以app.listen翻译下其实就是我们常见的下述写法:

app.listen = function listen() {
  var server = http.createServer(function(req,res,next){
  	app.handle(req,res,next)
  });
  return server.listen(arguments);
};
app.handle的源码在./application.js中:

app.handle = function handle(req, res, callback) {
  var router = this._router;

  // final handler
  var done = callback || finalhandler(req, res, {
    env: this.get('env'),
    onerror: logerror.bind(this)
  });

  // no routes
  if (!router) {
    debug('no routes defined on app');
    done();
    return;
  }

  router.handle(req, res, done);
};
该方法将监听到的用户请求转入router中处理,即第一阶段处理。

router.handle方法的分析下节继续分析。











  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值