• nodejs express 框架解密4-路由


    本文档是基于express3.4.6

    express 的路由是自己去实现的,没有使用connect中的路由中间件模块。

    1、在如何创建一个app那篇中,我们提到了路由,

      //router
      //路由
      this._router = new Router(this);
      this.routes = this._router.map;
      this.__defineGetter__('router', function(){
        this._usedRouter = true;
        this._router.caseSensitive = this.enabled('case sensitive routing');
        this._router.strict = this.enabled('strict routing');
        return this._router.middleware;
      });

    可以看到,是将Router这个类存储到了app上的_rounter这个属性上去了,然后将所有得路由映射存储到了routes 这个属性上了。最后在给app定义一个router属性,直接调用了这个模块的

    middleware模块。我们来看看Router类

    function Router(options) {
      options = options || {};
      var self = this;
      this.map = {};
      this.params = {};
      this._params = [];
      this.caseSensitive = options.caseSensitive;
      this.strict = options.strict;
      this.middleware = function router(req, res, next){
        self._dispatch(req, res, next);
      };
    }

    从上面的代码可以看出,middleware调用了自身的_dispatch 函数 ,这个函数的作用就是路由分发

    Router.prototype._dispatch = function(req, res, next){
      var params = this.params
        , self = this;
    
      debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
    
      // route dispatch
      (function pass(i, err){
        var paramCallbacks
          , paramIndex = 0
          , paramVal
          , route
          , keys
          , key;
    
        // match next route
        function nextRoute(err) {
          pass(req._route_index + 1, err);
        }
    
        // match route
        req.route = route = self.matchRequest(req, i);
        // implied OPTIONS
        if (!route && 'OPTIONS' == req.method) return self._options(req, res);
    
        // no route
        if (!route) return next(err);
        debug('matched %s %s', route.method, route.path);
    
        // we have a route
        // start at param 0
        req.params = route.params;
        keys = route.keys;
        i = 0;
    
        // param callbacks
        function param(err) {
          paramIndex = 0;
          key = keys[i++];
          paramVal = key && req.params[key.name];
          paramCallbacks = key && params[key.name];
    
          try {
            if ('route' == err) {
              nextRoute();
            } else if (err) {
              i = 0;
              callbacks(err);
            } else if (paramCallbacks && undefined !== paramVal) {
              paramCallback();
            } else if (key) {
              param();
            } else {
              i = 0;
              callbacks();
            }
          } catch (err) {
            param(err);
          }
        };
    
        param(err);
    
        // single param callbacks
        function paramCallback(err) {
          var fn = paramCallbacks[paramIndex++];
          if (err || !fn) return param(err);
          fn(req, res, paramCallback, paramVal, key.name);
        }
    
        // invoke route callbacks
        function callbacks(err) {
          var fn = route.callbacks[i++];
    
          try {
            if ('route' == err) {
              nextRoute();
            } else if (err && fn) {
              if (fn.length < 4) return callbacks(err);
              fn(err, req, res, callbacks);
            } else if (fn) {
              if (fn.length < 4) return fn(req, res, callbacks);
              callbacks();
            } else {
              nextRoute(err);
            }
          } catch (err) {
            callbacks(err);
          }
        }
      })(0);
    };

    这个函数是通过pass 这个自动执行的函数进行路由转发的,

    首先通过请求, req.route = route = self.matchRequest(req, i); 来配置路由,返回需要的信息

    可以看到matchRequest 函数返回了(我访问了下http://localhost:3000)

    { path: '/',
      method: 'get',
      callbacks: [ [Function] ],
      keys: [],
      regexp: /^//?$/i,
      params: []
     }

    看看 matchRequest 这个函数

    Router.prototype.matchRequest = function(req, i, head){
      var method = req.method.toLowerCase()
        , url = parse(req)
        , path = url.pathname
        , routes = this.map
        , i = i || 0
        , route;
    
      // HEAD support
      if (!head && 'head' == method) {
        route = this.matchRequest(req, i, true);
        if (route) return route;
         method = 'get';
      }
    
      // routes for this method
      if (routes = routes[method]) {
    
        // matching routes
        for (var len = routes.length; i < len; ++i) {
          route = routes[i];
          if (route.match(path)) {
            req._route_index = i;
            return route;
          }
        }
      }
    };

    它返回一个路由的处理结果。

    后面根据参数执行了param() 函数。这个函数主要是处理callback回调函数的。

    2.给路由注册各种函数:

    methods.forEach(function(method){
      app[method] = function(path){
        if ('get' == method && 1 == arguments.length) return this.set(path);
    
        // deprecated
        if (Array.isArray(path)) {
          console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0');
        }
    
        // if no router attached yet, attach the router
        if (!this._usedRouter) this.use(this.router);
    
        // setup route
        console.log(method,'test2');
        this._router[method].apply(this._router, arguments);
        return this;
      };
    });

    这个函数直接添加了开始的注册函数,下面的这个methods.foreach 依次为每一个app.get,app.post 等等 添加路由和 callback 函数

    Router.prototype.route = function(method, path, callbacks){
      var method = method.toLowerCase()
        , callbacks = utils.flatten([].slice.call(arguments, 2));
    
      // ensure path was given
      if (!path) throw new Error('Router#' + method + '() requires a path');
    
      // ensure all callbacks are functions
      callbacks.forEach(function(fn){
        if ('function' == typeof fn) return;
        var type = {}.toString.call(fn);
        var msg = '.' + method + '() requires callback functions but got a ' + type;
        throw new Error(msg);
      });
    
      // create the route
      debug('defined %s %s', method, path);
      var route = new Route(method, path, callbacks, {
        sensitive: this.caseSensitive,
        strict: this.strict
      });
    
      // add it
      (this.map[method] = this.map[method] || []).push(route);
      return this;
    };
    
    Router.prototype.all = function(path) {
      var self = this;
      var args = [].slice.call(arguments);
      methods.forEach(function(method){
          self.route.apply(self, [method].concat(args));
      });
      return this;
    };
    
    methods.forEach(function(method){
      Router.prototype[method] = function(path){
        var args = [method].concat([].slice.call(arguments));
        this.route.apply(this, args);
        return this;
      };
    });

    最后一个函数,直接执行,给路由添加get,post等函数。

    在Router.prototype.route  函数中,我们调用了一个:

    var route = new Route(method, path, callbacks, {
        sensitive: this.caseSensitive,
        strict: this.strict
      });

    Route 是一个路由的基本单元,包含2个方法:

    function Route(method, path, callbacks, options) {
      options = options || {};
      this.path = path;
      this.method = method;
      this.callbacks = callbacks;
      this.regexp = utils.pathRegexp(path
        , this.keys = []
        , options.sensitive
        , options.strict);
    }
    
    /**
     * Check if this route matches `path`, if so
     * populate `.params`.
     *
     * @param {String} path
     * @return {Boolean}
     * @api private
     */
    
    Route.prototype.match = function(path){
      var keys = this.keys
        , params = this.params = []
        , m = this.regexp.exec(path);
    
      if (!m) return false;
    
      for (var i = 1, len = m.length; i < len; ++i) {
        var key = keys[i - 1];
    
        var val = 'string' == typeof m[i]
          ? utils.decode(m[i])
          : m[i];
    
        if (key) {
          params[key.name] = val;
        } else {
          params.push(val);
        }
      }
    
      return true;
    }; 
  • 相关阅读:
    Freesql使用一张带父子关系的表,生成list树型数据
    css特效实现html表格显示部分内容,当鼠标移上去显示全部。
    ASP.NET Core如何自动生成小写的破折号路由
    windows服务+OpenSSH+SVN在Jenkins上的自动化部署进阶篇
    Windows+.Net Framework+svn+IIS在Jenkins上的自动化部署入门
    .net(C#)中结构和类的区别
    Web.config配置详解
    关于Asp.net core配置信息读取的源码分析梳理
    极简实用的Asp.NetCore框架再新增商城模块
    极简实用的Asp.NetCore模块化框架新增CMS模块
  • 原文地址:https://www.cnblogs.com/yupeng/p/3482271.html
Copyright © 2020-2023  润新知