express 的 middleware 设想

还没用express写过server,先把部份源码撸了一遍,列位大神求轻拍。

express进口文件在lib文件夹下的express.js,其向外界暴露了一些要领。

最主要的(express.js 第36-47行):

function createApplication() {
  var app = function(req, res, next) {
    app.handle(req, res, next);         //各中心件的处置惩罚进口,handle要领经由过程mixin拓展于proto
  };

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

  app.request = { __proto__: req, app: app };
  app.response = { __proto__: res, app: app };
  app.init();
  return app;
}

exports = module.exports = createApplication;

我们经常在本身的营业代码中如许写:

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

实在就是挪用createApplication要领.如许就实例化了一个app。这个app比较特别,经由过程mixin集成了一些其他的属性

mixin(app, EventEmitter.prototype, false); //拓展了事宜发射器原型对象
mixin(app, proto, false); //拓展了application.js中的属性和要领

在我们营业代码实例化app的时刻,挪用了app.init()要领完成了一些初始化的设置。init()要领也是从application.js中继续的。

进口文件很清楚,主假如完成要领的暴露以及app的一些初始化操纵。

接下来看下application.js中的部份代码逻辑:

第136-146行,耽误实例化一个_router

app.lazyrouter = function lazyrouter() {
  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));
  }
};

第157-174行,这是各个middleware的进口,

app.handle = function handle(req, res, callback) {
  var router = this._router;    //猎取已实例化得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);    //当http过来时,关于request和response的处置惩罚从这个处所最先
};

第187-242行,app.use要领供应了运用级的middleware,然则事实上在214行,this.lazyrouter()新建一个route,第219-221行,然后依据app.use(fn)传入的参数挂载到了route.use()路由级中心件上了。app.use()route.use的一个代办。

app.use = function use(fn) {
  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)); //摊平arguments

  if (fns.length === 0) {
    throw new TypeError('app.use() requires middleware functions');
  }

  // setup router
  this.lazyrouter();                                //假如没有route实例则新建一个
  var router = this._router;

  fns.forEach(function (fn) {
    // non-express app                              //假如传入的不是express实例app
    if (!fn || !fn.handle || !fn.set) {
      return router.use(path, fn);                  //将中心件注入到router中
    }

    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) {          
        req.__proto__ = orig.request;
        res.__proto__ = orig.response;
        next(err);
      });
    });

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

  return this;
};

第255-258行,代办到router实例的route()的要领中:

    app.route = function route(path) {
        this.lazyrouter();
        return this._router.route(path);
    };

router实例是经由过程router/index.js供应组织函数来建立的,在这个文件夹中第42-60行:

var proto = module.exports = function(options) {
  var opts = options || {};

  function router(req, res, next) {
    router.handle(req, res, next);    //handle要领继续于proto
  }

  // mixin Router class functions
  router.__proto__ = proto;

  router.params = {};
  router._params = [];
  router.caseSensitive = opts.caseSensitive;
  router.mergeParams = opts.mergeParams;
  router.strict = opts.strict;
  router.stack = [];        //初始化一个stack.这个stack中保留了注册的一切中心件

  return router;
};

供应了一个router的组织函数,它的原型对象上,第136行供应了proto.handle要领,这个要领的作用就是吸收来自httpreqres

第413行,供应了proto.use要领,正如上面所说的app.useroute.use的代办,这个要领的特别性就在任何的http要求都邑经由在app.use上挂载的中心件,比方如今express4.x已将许多中心件从本身移除,须要你从新经由过程npm去装置,然后在营业代码中举行援用,比方运用body-parser中心件:

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json());

如许每次http要求过来的时刻起首会经由bodyParser.json()这个中心件,它供应了一个向req增加req.body = {}要领,并传向下一个中心件的作用。
同时在route.use内部,第439-458行,

for (var i = 0; i < callbacks.length; i++) {
    var fn = callbacks[i];

    if (typeof fn !== 'function') {
      throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn));
    }

    // add the middleware
    debug('use %s %s', path, fn.name || '<anonymous>');

    var layer = new Layer(path, {       //新建一个layer,layer上挂载了error_handler和request_handler
      sensitive: this.caseSensitive,
      strict: false,
      end: false
    }, fn);

    layer.route = undefined;            

    this.stack.push(layer);             //route本身会保护一个stack,将每一个新建的layer都推入stack当中,这个layer实例终究会对婚配的path,作出error_handle或许request_handle。
  }

第477行,proto.route要领供应了一个新建route的要领。

proto.route = function route(path) {
  var route = new Route(path);      //新建一个route,这个Route构建函数内部完成见./route.js,它内里供应了一个空的stack,用以

  var layer = new Layer(path, {                     //新建一个layer,layer的作用见下面的解说
    sensitive: this.caseSensitive,
    strict: this.strict,
    end: true
  }, route.dispatch.bind(route));

  layer.route = route;  

  this.stack.push(layer);   //新建一个route,这个route会保护本身的stack
  return route;
};

var route = require('express').Router(),然则这个要领差别的处所
在于,它会本身保护一个stack,这个stack中有你在这个要领上面定义的一切中心件。一样,你能够经由过程这个route挂载关于差别途径的req, res的处置惩罚。

运用的要领:

var express = require('express');
var app = express();
var router = express.Router();

//没有挂载任何途径的中心件,经由过程该路由的每一个要求都邑实行该中心件
router.use(function(req, res, next) {
    console.log('route.use');
})


router.get('/test', function(req, res, next) {
    console.log('route.get'); 
});

//末了须要将这个router挂载到运用
app.use('/', router);

以上部份主假如全部express的中心件的挂载。总结一下:

  1. 经由过程app.use()挂载的中心件终究都代办到了router.use()要领下

  2. router.use()要领,新建一个layer,layer上保留了途径,默以为'/',及响应的处置惩罚要领,并存入这个app保护的stack中。

  3. 经由过程var router = require('express').Router()新建的router途径级实例,一样能够挂载差别的中心件,不过末了须要将这个router路由注入到app运用当中:app.use('/', router);

接下来说下当http要求到来的时刻,数据的流向: 在你定义中心件的过程当中,由于是保护了一个app或许route实例,它们离别都有一个stack。这个stackFIFO的,因而每当一个要求过来的时刻,数据从最最先的定义的中心件最先,一向向下按递次举行通报,因而你能够本身定义,固然,你须要挪用next()要领。就比方Route.protoype.dispath要领

//将req, res分发给这个route
Route.prototype.dispatch = function dispatch(req, res, done) {
  var idx = 0;
  var stack = this.stack;
  if (stack.length === 0) {
    return done();
  }

  var method = req.method.toLowerCase();
  if (method === 'head' && !this.methods['head']) {
    method = 'get';
  }

  req.route = this;

  next();

  function next(err) {
    if (err && err === 'route') {
      return done();
    }

    var layer = stack[idx++];
    if (!layer) {
      return done(err);
    }

    if (layer.method && layer.method !== method) {  //婚配传入的req要求体式格局,和layer的method举行对照
      return next(err);
    }

//挪用layer.handle,用以错误处置惩罚或许request处置惩罚
    if (err) {
      layer.handle_error(err, req, res, next);      
    } else {
      layer.handle_request(req, res, next);         
    }
  }
};

末了,http要求的处置惩罚:
app或许route实例中,本身有一个stack,这个stack就存放了在挂载中心时新建的layer,每一个layer实例都保留了对应的途径,以及响应的error_handlerequest_handle

感谢人人看到这里,迎接人人指正。

下一篇写写express路由的完成。

    原文作者:苹果小萝卜
    原文地址: https://segmentfault.com/a/1190000006695641
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞