• express源码剖析4


    请求过程:express/appliction.js

    server服务器启动:

    //两个参数,端口和机器IP
    app.listen(port, host);
    app.listen = function listen() {
      /**
      this就是app函数、也是对象。
      var app = function(req, res, next) {
        app.handle(req, res, next);
      };
      */
      var server = http.createServer(this);
      /*
       server为http.server的实例。
      */
      return server.listen.apply(server, arguments);
    };

    当服务器启动之后,它调用application.js中handler函数,执行过程是进入index.js中proto.handle函数,一个请求过来执行的入口是:

    var app = function(req, res, next) {
        //application.js中的handle函数
        app.handle(req, res, next); 
    };
    app.handle = function handle(req, res, callback) {
        var router = this._router;
        //一般来说next函数为没有的,在handler函数中封装了一个默认done,也就是callback,如下:
        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);
    };
    

    在进入proto.handle函数之前,先看看两个包装器函数restore和wrap的功能:

    1.restore和wrap

    // manage inter-router variables baseUrl、url都是子app绑定到use(path,subApp)利用到的。
    var parentParams = req.params; //初始时为undefined 
    var parentUrl = req.baseUrl || ''; //初始时为""
    //给baseurl和params重新赋值。
    var done = restore(out, req, 'baseUrl', 'next', 'params');
    function restore(fn, obj) {
      var props = new Array(arguments.length - 2);
      var vals = new Array(arguments.length - 2);
      for (var i = 0; i < props.length; i++) {
        props[i] = arguments[i + 2];  // 储存除fn,obj以外的参数
        vals[i] = obj[props[i]];      // obj中的对象。'baseUrl', 'next', 'params'
      }
      return function(err){
        // restore vals
        for (var i = 0; i < props.length; i++) {
          obj[props[i]] = vals[i];
        }
        return fn.apply(this, arguments);
      };
    }
    /*out是finalhandler(req, res, {
        env: this.get('env'),
        onerror: logerror.bind(this)
      });
    */

    wrap函数

    // for options requests, respond with a default if nothing else responds
    if (req.method === 'OPTIONS') {
        done = wrap(done, function(old, err) {
          if (err || options.length === 0) return old(err);
          sendOptionsResponse(res, options, old);
        });
    }//wrap是封装了旧的done函数,也就是next函数,执行的是fn.apply(this,args);
    function wrap(old, fn) {
      return function proxy() {
        var args = new Array(arguments.length + 1);
        args[0] = old;
        for (var i = 0, len = arguments.length; i < len; i++) {
          args[i + 1] = arguments[i];
        }
        fn.apply(this, args);
      };
    }
    // send an OPTIONS response
    function sendOptionsResponse(res, options, next) {
      try {
        var body = options.join(',');
        res.set('Allow', body);
        res.send(body);
      } catch (err) {
        next(err);
      }
    }

    2.proto.handle

      var self = this;
    
      debug('dispatching %s %s', req.method, req.url);
      //req.url不含域名:类似/m/login.html,第一个字符为“/”
      var search = 1 + req.url.indexOf('?');
      //URL含?則path長度為search - 1
      var pathlength = search ? search - 1 : req.url.length;
      //fqdn为true,只有可能是完整的url,如:http://www.baidu.com/dir/xy.html,其他路徑
      //如:/dir/put.json或者:jj/tt.json都只能是 false
      var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://');
      //protohost就是主机的名字,去掉协议
      var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
    
      var idx = 0;
      var removed = '';
      var slashAdded = false;
      var paramcalled = {};
    
      // store options for OPTIONS request
      // only used if OPTIONS request
      var options = [];
    
      // middleware and routes
      var stack = self.stack;
    
      // manage inter-router variables
      // params和baseUrl都是undefined
      var parentParams = req.params;
      var parentUrl = req.baseUrl || '';
      var done = restore(out, req, 'baseUrl', 'next', 'params');
      // setup next layer
      req.next = next;
      // for options requests, respond with a default if nothing else responds
      // options請求方式,重新定義done
      if (req.method === 'OPTIONS') {
        done = wrap(done, function(old, err) {
          if (err || options.length === 0) return old(err);
          sendOptionsResponse(res, options, old);
        });
      }
    
      // setup basic req values
      req.baseUrl = parentUrl;
      req.originalUrl = req.originalUrl || req.url;

     下面的next会递归遍历router和中间件 

      next();
      function next(err) {
        //函数是个递归,功能:1.还原req.url,2.终止递归,3.遍历stack,匹配layer
        //初始化时err为undefined,err = "route"的情况在于try、catch抛出的异常。
        var layerError = err === 'route' ? null : err;
        // remove added slash,
        if (slashAdded) {
          //如果url="/m/xx.html",那么url为"m/xx.html";
          req.url = req.url.substr(1);
          slashAdded = false;
        }
        // restore altered req.url,还原req.url,
        if (removed.length !== 0) {
          req.baseUrl = parentUrl;
          req.url = protohost + removed + req.url.substr(protohost.length);
          removed = '';
        }
        // no more matching layers,递归终止,
        if (idx >= stack.length) {
          //router中stack的Layer已经遍历完毕,立即执行回调函数done.
          setImmediate(done, layerError);
          return;
        }
        // get pathname of request
        var path = getPathname(req);
        //没有获取到req中的pathname,那么它会执行递归终止,
        if (path == null) {
           // 结果是:out.apply(undefined,undefined);最终转向finalhandler执行完毕,
          return done(layerError);
        }
        // find next matching layer
        var layer;
        var match;
        var route;
        //要匹配成功才能跳出循环
        while (match !== true && idx < stack.length) {
          layer = stack[idx++];
    //会抛出'router'错误 match
    = matchLayer(layer, path);
    //通过app.use来定义的layer.route=undefined,通过router.route(path)来定义的layer.route不为undefine. route
    = layer.route; if (typeof match !== 'boolean') { // hold on to layerError layerError = layerError || match; //出错,layerError可能是'router' }
    //没有匹配到,或者匹配出错、出错可能是'router'
    if (match !== true) { continue; }
    //处理针对router.router来挂载的处理函数。
    if (!route) { // process non-route handlers normally continue; } //如果route不是undefined,那么继续,同时在处理this.params对应key的函数出错,那么即使匹配到了下一个Layer,也会跳出next, if (layerError) { // routes do not match with a pending error match = false; continue; } var method = req.method; //匹配到Layer所对应的router支持的方法 var has_method = route._handles_method(method); // build up automatic options response if (!has_method && method === 'OPTIONS') { //支持options请求方法。 appendMethods(options, route._options()); } // don't even bother matching route,请求时head,不需要返回。 if (!has_method && method !== 'HEAD') { match = false; continue; } } // 没有匹配到Layer,递归终止, if (match !== true) { return done(layerError); } // store route for dispatch on change if (route) { req.route = route; } // Capture one-time layer values,合并匹配到layer中path的参数对象,可以查看api。 req.params = self.mergeParams ? mergeParams(layer.params, parentParams) : layer.params;
    //含正则,不是实际请求的path
    var layerPath = layer.path; // this should be done for the layer,如果说请求的url中带有参数,处理每个参数绑定的事情。 self.process_params(layer, paramcalled, req, res, function(err) {
    //在paramCallback出现err,但是这个err绝对不是"router"。
    if (err) { return next(layerError || err); //一个匹配router.stack中的一个Layer结束、进入stack的下一个元素进行匹配。 }
    //通过route.route来定义的Layer。带有Layer.router = router.
    if (route) { return layer.handle_request(req, res, next); } trim_prefix(parentParams, layerError, layerPath, path); }); }

     继续看process_params的处理函数:

    proto.process_params = function process_params(layer, called, req, res, done) {
      var params = this.params;
      // captured parameters from the layer, keys and values
      var keys = layer.keys;
    
      // fast track,  //沒有參數
      if (!keys || keys.length === 0) {
        return done();
      }
      var i = 0;
      var name;
      var paramIndex = 0;
      var key;
      var paramVal;
      var paramCallbacks;
      var paramCalled;
      //param和next函數的處理方式很類似。看代碼也忽略err的情況,
      // process params in order
      // param callbacks can be async,
      function param(err) {
        if (err) {
          return done(err);
        }
    
        if (i >= keys.length) {
          return done();
        }
    
        paramIndex = 0;
        //m=["/user/anthonyliu/your","anthonyliu","your"]
        key = keys[i++]; //m = /user/anthonyliu/your
    
        if (!key) {
          return done();
        }
    
        name = key.name;
        //区别params和req.params。params為this.params,也就是router.params。对应的是{key1:[f1,f2],key2:[f3,f4]....}。
        //paramsCallbacks是通過router.params綁定的數組。
        paramCallbacks = params[name];
        //初始化時called[name]為undefined 
        paramCalled = called[name];
        if (paramVal === undefined || !paramCallbacks) {
          //
          return param();
        }
        // param previously called with same value or error occurred
        // 正常情況下,調用一次就不需要調用了,或者發送錯誤,錯誤不等於‘route’
        if (paramCalled && (paramCalled.match === paramVal || (paramCalled.error && paramCalled.error !== 'route'))) {
          // restore value
          // 为什么要重新存储value?因为在req.params对象中相同的key可能会有不同的value
          // 例如:第一层和第二层Layer的key相同而value不一样
          req.params[name] = paramCalled.value;
          // next param,出錯會跳出递归!否則相同key对应相同的value所绑定的处理函数會跳過。
          return param(paramCalled.error);
        }
    
        called[name] = paramCalled = {
          error: null,
          match: paramVal,
          value: paramVal
        };
    
        paramCallback();
      }
    
      // single param callbacks
      function paramCallback(err) {
        var fn = paramCallbacks[paramIndex++];
        // store updated value,
        paramCalled.value = req.params[key.name];
        if (err) {
          // store error
          paramCalled.error = err;
          param(err);
          return;
        }
        if (!fn) return param();
        try {
    //可能会重写 req.params[key.name],从而会导致需要 fn(req, res, paramCallback, paramVal, key.name); }
    catch (e) {
    //err有两种值,一是"router",它不影响params以后的函数调用;另一个是“router”,它会终止params以后的函数调用。 paramCallback(e); } } param(); };
     

    trim_prefix

    //没有看懂时,这段代码要人命,看懂了都不好意思写注释了
    function trim_prefix(layer, layerError, layerPath, path) {
        //path是请求的url,layerPath为Layer的路径匹配的字符串。
        var c = path[layerPath.length];
        if (c && '/' !== c && '.' !== c) return next(layerError);
        // Trim off the part of the url that matches the route
        // middleware (.use stuff) needs to have the path stripped
        //形成req.baseUrl = req.parentUrl + req.url或者req.url = req.originalUrl - req.baseUrl
        if (layerPath.length !== 0) {
          debug('trim prefix (%s) from url %s', layerPath, req.url);
          removed = layerPath;
          req.url = protohost + req.url.substr(protohost.length + removed.length);
    
          // Ensure leading slash
          if (!fqdn && req.url[0] !== '/') {
            req.url = '/' + req.url;
            slashAdded = true;
          }
    
          // Setup base URL (no trailing slash)
          req.baseUrl = parentUrl + (removed[removed.length - 1] === '/' ? removed.substring(0, removed.length - 1) : removed);
        }
    
        debug('%s %s : %s', layer.name, layerPath, req.originalUrl);
    
        if (layerError) {
          layer.handle_error(layerError, req, res, next);
        } else {
          layer.handle_request(req, res, next);
        }
      }

    基本上到这里,express源码就结束了。

  • 相关阅读:
    SEO优化---学会建立高转化率的网站关键词库
    从一个程序员的角度看——微信小应用
    当AngularJS POST方法碰上PHP
    angularJS(6)
    彻底解决显示Opencv中Mat图像到Mfc窗口问题
    数据结构与算法基础总结
    java类别问题
    java基础知识
    逻辑地址、线性地址、物理地址和虚拟地址的区别
    TCP协议中的三次握手和四次挥手(图解)
  • 原文地址:https://www.cnblogs.com/liuyinlei/p/6599452.html
Copyright © 2020-2023  润新知