expressjs 源码

Published: · LastMod: April 07, 2024 · 1226 words

expressjs 源码 🔗

createApplication 🔗

暴露应用逻辑

1
exports = module.exports = createApplication;

express.js创建应用主体逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
function createApplication() {
  var app = function(req, res, next) {
    app.handle(req, res, next);
  };

  mixin(app, EventEmitter.prototype, false);
  mixin(app, proto, false);

  // expose the prototype that will get set on requests
  app.request = Object.create(req, {
    app: { configurable: true, enumerable: true, writable: true, value: app }
  })

  // expose the prototype that will get set on responses
  app.response = Object.create(res, {
    app: { configurable: true, enumerable: true, writable: true, value: app }
  })

  app.init();
  return app;
}

mixin通过扩展属性的方式扩展这里app上的方法 这里分别扩展了EventEmitter.prototypeproto proto就是application暴露出的内容

application.js 🔗

输出app这样的一个对象

1
var app = exports = module.exports = {};

对象中通过对空对象的属性添加,增加一系列方法

  • init
  • defaultConfiguration
  • lazyrouter
  • handle
  • use
  • route
  • engine
  • param
  • set
  • path
  • enabled
  • disabled
  • METHOD方式扩展
  • all
  • del // 已删除
  • render
  • listen

lazyrouter 🔗

初始化_router, 其实就是实例化Router

1
2
3
4
5
6
7
8
9
if (!this._router) {
    this._router = new Router({
      caseSensitive: this.enabled('case sensitive routing'),
      strict: this.enabled('strict routing')
    });

    this._router.use(query(this.get('query parser fn')));
    this._router.use(middleware.init(this));
  }

这里初始化的时候,就会增加2个初始的中间件

  • init中间件
  • query中间件

init 🔗

init中间件,增加了一个powered-by头,可配置 把res赋值给req.res 把req赋值给res.req 把next复制给req.next 套娃。。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
init = function(app){
  return function expressInit(req, res, next){
    if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express');
    req.res = res;
    res.req = req;
    req.next = next;

    setPrototypeOf(req, app.request)
    setPrototypeOf(res, app.response)

    res.locals = res.locals || Object.create(null);

    next();
  };
};

query 🔗

query中间件的主要作用就是给请求增加一个query属性,通过queryparse方法进行格式化

1
2
3
4
5
6
7
8
return function query(req, res, next){
    if (!req.query) {
      var val = parseUrl(req).query;
      req.query = queryparse(val, opts);
    }

    next();
  };

use 🔗

  • 如果 use方法传入的参数是一个数组, 这里要做参数截取,目的就是拿到所有参数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
  var offset = 0;
  var path = '/';

  // default path to '/'
  // disambiguate app.use([fn])
  if (typeof fn !== 'function') {
    var arg = fn;

    while (Array.isArray(arg) && arg.length !== 0) {
      arg = arg[0];
    }

    // first arg is the path
    if (typeof arg !== 'function') {
      offset = 1;
      path = fn;
    }
  }

  var fns = flatten(slice.call(arguments, offset));

递归所有函数, 添加路径拦截方法, 调用fn.handle方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  fns.forEach(function (fn) {
    // non-express app
    if (!fn || !fn.handle || !fn.set) {
      return router.use(path, fn);
    }

    debug('.use app under %s', path);
    fn.mountpath = path;
    fn.parent = this;

    // restore .app property on req and res
    router.use(path, function mounted_app(req, res, next) {
      var orig = req.app;
      fn.handle(req, res, function (err) {
        setPrototypeOf(req, orig.request)
        setPrototypeOf(res, orig.response)
        next(err);
      });
    });

    // mounted an app
    fn.emit('mount', this);
  }, this);

param 🔗

应用主体上绑定param方法,在应用请求过程中如果有相应的参数,就会回调对应的回调函数

1
2
3
4
app.param("name", (req, res, next) => {
  // .....
  
})

主体实现还是在router中完成的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function param(name, fn) {
  this.lazyrouter();

  if (Array.isArray(name)) {
    for (var i = 0; i < name.length; i++) {
      this.param(name[i], fn);
    }

    return this;
  }

  this._router.param(name, fn);

  return this;
};

path 🔗

通过调用app.path()方法 可以输出当前请求的绝对路径,这里会递归调用,如果存在父级,会拿到父级路径并连接

1
2
3
  return this.parent
    ? this.parent.path() + this.mountpath
    : '';

handle 🔗

handle这里调用了router的handle方法 done方法进行了重写

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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);
};

set 🔗

1
app.set('foo', 'bar')

set方法往主体应用setting对象添加属性

1
this.settings[setting] = val

如果只传入了一个参数,默认是get获取值

all 🔗

对所有请求方式进行调用

1
2
3
4
5
6
7
8
  var route = this._router.route(path);
  var args = slice.call(arguments, 1);

  for (var i = 0; i < methods.length; i++) {
    route[methods[i]].apply(route, args);
  }

  return this;