zoukankan      html  css  js  c++  java
  • Node.js express路由简单分析

    这2天看了一点node+express的路由源码有了一点眉目,总结一下

    对于app.get,

    首先给出一张类图:

    图1

    注意每个路由有一个stack,这个stack中存放了Layer。

    路由系统内有三个文件:

    图2

    其中layer.js,route.js即为图1中的类的模块。

    application.js都只对外层router进行操作

    外层路由针对中间件来说的,内层路由针对中间件的链路来说

    在index.js中,我认为又对Route做了封装,下面是即为重要的工厂方法

    proto.route = function route(path) {
    //外层路由构建
    var route = new Route(path);//创建内层Route var layer = new Layer(path, { sensitive: this.caseSensitive, strict: this.strict, end: true }, route.dispatch.bind(route)); layer.route = route;//每个外层layer引用一个内层路由 形成循环引用 this.stack.push(layer);
    //将构建得到的layer加入外层stack
    return route; };

    //外层route的所有HTTP谓词方法的实现
    //
    methods.concat('all').forEach(function(method){ proto[method] = function(path){ var route = this.route(path)//外层加一层 route[method].apply(route, slice.call(arguments, 1));//调用里层Router谓词 里层加一层 return this; }; });
    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));
      }
    };

    
    
    app.use中间件的实现,可见都用到了路由系统的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));
    
      if (fns.length === 0) {
        throw new TypeError('app.use() requires middleware functions');
      }
    
      // setup router
      this.lazyrouter();
      var router = this._router;
    
      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) {
            req.__proto__ = orig.request;
            res.__proto__ = orig.response;
            next(err);
          });
        });
    
        // mounted an app
        fn.emit('mount', this);
      }, this);
    
      return this;
    };

    可见调用的是外层路由的use方法,再来看看路由的use方法:(index.js)

    proto.use = function use(fn) {
      var offset = 0;
      var path = '/';
    
      // default path to '/'
      // disambiguate router.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 callbacks = flatten(slice.call(arguments, offset));
    
      if (callbacks.length === 0) {
        throw new TypeError('Router.use() requires middleware functions');
      }
    
      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, {
          sensitive: this.caseSensitive,
          strict: false,
          end: false
        }, fn);
      //外层路由构建
        layer.route = undefined;
    
        this.stack.push(layer);
      }
    
      return this;
    };

    可见每个layer对应一个函数,一个stack中有多个layer。并将path传入了Layer中,每次调用use都会创建一个外层layer.

    再来看看app.get/post等方法

    methods.forEach(function(method){
      app[method] = function(path){
        if (method === 'get' && arguments.length === 1) {
          // app.get(setting)
          return this.set(path);
        }
    
        this.lazyrouter();
    
        var route = this._router.route(path);//外层增加,增加一个外层Layer
        route[method].apply(route, slice.call(arguments, 1));
        return this;
      };
    });

    methods为所有HTTP谓词等的数组,所有的app.METHOD都适用里层Router的谓词

    ,调用内层Router,对于内层Router的HTTP谓词:

    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
          layer.method = method;
    
          this.methods[method] = true;
          this.stack.push(layer);//内层Layer
        }
    
        return this;
      };
    });

    所有的get/post等都是在内层Router的stack上增加Layer

    可见不管是use,还是get/post 最后都是在增加Layer,对于use,增加外层layer,对于get/post,既增加外层layer,又增加内层layer

  • 相关阅读:
    自动化测试如何解析excel文件?
    Unittest加载执行用例的方法总结
    pytest进阶之配置文件
    [编程题] 把二叉树打印成多行
    [编程题]求1+2+3+....n
    [编程题]-[位运算技巧系列]不用加减乘除做加法
    [编程题]数值的整数次方
    [编程题]构建乘积数组
    [编程题]变态跳台阶
    [编程题][剑指 Offer 10- II. 青蛙跳台阶问题]
  • 原文地址:https://www.cnblogs.com/07lyt/p/5397219.html
Copyright © 2011-2022 走看看