码迷,mamicode.com
首页 > 其他好文 > 详细

express源码剖析(3)

时间:2017-01-12 09:05:28      阅读:520      评论:0      收藏:0      [点我收藏+]

标签:for   cte   没有   standard   cti   his   模板   接下来   disable   

express/appliction.js

application.js对外的方法可以分成四类:

  • 设置/初始化成员变量,主要是对成员变量settings进行设置,这样的方法有:enabled、enabled、disabled、 disable和set方法。
  • 设置路由和对每个路由设置中间件:这样的方法有:use.router,all,param和methods方法。
  • 设置模板引擎的方法:engine和render。
  • 启动node服务器对res和req操作的方法:listen和handler。

1.解析app.engine方法

express的api中有app.engine方法,例如:

app.engine("handlebars",handlebars.engine);

看下这个函数的的核心代码就是:

 // get file extension,ext为文件扩展名
  var extension = ext[0] !== ‘.‘
    ? ‘.‘ + ext
    : ext;

  // store engine
  this.engines[extension] = fn;

 1.2 解析app.use方法

var express = require(‘express‘);
var app = express();
function handlerWrap(){
    console.log("1");
}
app.use("/", handlerWrap);

代码直接进入app.user中的下列代码:

//lazyrouter函数不对外提供
this.lazyrouter();  

 

它可以说是整个express的核心代码:

app.lazyrouter = function lazyrouter() {
    //给app这个对象定义私有变量_router,它是一个对象Router。
    if (!this._router) {
       this._router = new Router({
        caseSensitive: this.enabled(‘case sensitive routing‘), //false
        strict: this.enabled(‘strict routing‘)                 //false 
      });
      this._router.use(query(this.get(‘query parser fn‘)));     
       //this.get(‘query parser fn‘)为一个函数:
//
middleware.init(this)也是一个处理函数
      this._router.use(middleware.init(this));
   }
 };    

 

尤其是给app.router赋值之后,初始化下列代码:

this._router.use(query(this.get(‘query parser fn‘)));  //this.get(‘query parser fn‘)为一个函数:
this._router.use(middleware.init(this));
//必须要值得注意的事情是:router.use(path,fn); 实际上是为path建立layer,有多少个fn,就建立多少个Layer,然后把这个layer压入router.stack数组中。

 

看看router的构造函数,它的定义和app的定义很类似。

 if (!fn || !fn.handle || !fn.set) {
      //handlerWrap函数为handler、 set成员!
      return router.use(path, fn);
 }
// 进入router/index.js中,执行proto.use()方法。
// 实际上是给router.stack添加一个layer成员,

  var layer = new Layer(path, {
    sensitive: this.caseSensitive,
    strict: false,
    end: false
  }, fn);

  layer.route = undefined;  //给layer.route赋值undefined

   this.stack.push(layer);

1.3 解析router.route

 

 

ayer.js作为中间件封装的数据结构,接着看Layer包:

function Layer(path, options, fn) {
  if (!(this instanceof Layer)) {
    // this是Layer的实例
    return new Layer(path, options, fn);
  }
  
  debug(‘new %s‘, path);
  var opts = options || {};

  this.handle = fn;
  this.name = fn.name || ‘<anonymous>‘;
  this.params = undefined;
  this.path = undefined;
  this.regexp = pathRegexp(path, this.keys = [], opts);

  if (path === ‘/‘ && opts.end === false) {
  
this.regexp.fast_slash = true; } }

接下来看看pathRegexp,http://blog.csdn.net/chszs/article/details/51055229

rounter.use直接把该函数构造成一个Layer成员,实际上是给router.stack添加一个layer成员。

同理:this._router.use(middleware.init(this))的作用是把该函数:

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;

    req.__proto__ = app.request;
    res.__proto__ = app.response;

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

    next();
  };

 

构造成一个Layer成员,实际上是给router.stack添加一个layer成员。

这样在express初始的时候,rounter的stack数组中有两个回调函数了。如果说调用app.user(fn),都是给rounter的stack数组中添加回调函数。

执行过程:

执行的入口函数是:

var app = function(req, res, next) {
    app.handle(req, res, next);
  };

 

它调用application.js中handler函数,一般来说next函数为没有的,在handler函数中封装了一个callback:

// final handler, 默认就是next
  var done = callback || finalhandler(req, res, {
    env: this.get(‘env‘),
    onerror: logerror.bind(this)
  });

 

finalhandler是一个npm包,可以在github上看它的作用,它的作用就是一个http请求的最后一步的处理方式。

接着请求会转向rounter.handler函数,在handler函数中,最终是执行next(),在看next()之前,先来弄清除几个回调函数,第一个:done:

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)
  });
*/
var done = restore(out, req, ‘baseUrl‘, ‘next‘, ‘params‘);
/*
  done = function(err){
    给req赋值,重新储存‘baseUrl‘, ‘next‘, ‘params‘,
返回fn.apply(this,arguments);
  }
*/

 

next()函数中如果没有获取到req中的pathname,那么它会执行:

if (path == null) {
      //layerError为undefined
      return done(layerError);   
}
/*
   结果是:out.apply(undefined,undefined);最终转向finalhandler执行完毕 
*/

 

最后,重点弄清楚的代码是:

while (match !== true && idx < stack.length) {
      layer = stack[idx++]; //第一个Layer
      match = matchLayer(layer, path); //
      route = layer.route;

      if (typeof match !== ‘boolean‘) { 
        // hold on to layerError
        layerError = layerError || match;
      }

      if (match !== true) {
        continue;
      }

      if (!route) {
        // process non-route handlers normally
        continue;
      }

      if (layerError) {
        // routes do not match with a pending error
        match = false;
        continue;
      }

      var method = req.method;
      var has_method = route._handles_method(method);

      // build up automatic options response
      if (!has_method && method === ‘OPTIONS‘) {
        appendMethods(options, route._options());
      }

      // don‘t even bother matching route
      if (!has_method && method !== ‘HEAD‘) {
        match = false;
        continue;
      }
    }

    // no match
    if (match !== true) {
      return done(layerError);
    }

    // store route for dispatch on change
    if (route) {
      req.route = route;
    }

    // Capture one-time layer values
    req.params = self.mergeParams
      ? mergeParams(layer.params, parentParams)
      : layer.params;
    var layerPath = layer.path;

    // this should be done for the layer
    self.process_params(layer, paramcalled, req, res, function (err) {
      if (err) {
        return next(layerError || err);
      }

      if (route) {
        return layer.handle_request(req, res, next);
      }

      trim_prefix(layer, layerError, layerPath, path);
    });
  }

 

看看Layer.matchLayer(layer, path),他返回是true/false;不过会给该Layer添加:params = {};path 和keys。

重点代码:

fns.forEach(function (fn) {
    // non-express app,fn && fn.handler && fn.set为true
    if (!fn || !fn.handle || !fn.set) {
      return router.use(path, fn);
    }

    debug(‘.use app under %s‘, path);
    //给fn添加两个变量
    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);

1.2 send函数解析:

技术分享
 1 function send(req, res, status, body) {
 2   function write() {
 3     res.statusCode = status
 4 
 5     // security header for content sniffing
 6     res.setHeader(‘X-Content-Type-Options‘, ‘nosniff‘)
 7 
 8     // standard headers
 9     res.setHeader(‘Content-Type‘, ‘text/html; charset=utf-8‘)
10     res.setHeader(‘Content-Length‘, Buffer.byteLength(body, ‘utf8‘))
11     //请求方法为head,不需要发送body,header的作用多用于自动搜索机器人获取网页的标志信息,或者rss种子信息,或者传递安全认证信息等。
12     if (req.method === ‘HEAD‘) {
13       res.end()
14       return
15     }
16 
17     res.end(body, ‘utf8‘)
18   }
19 
20   if (isFinished(req)) {      //req流已经结束
21     write()                       //给res写入信息,并且返回给客户端
22     return
23   }
24 
25   // unpipe everything from the request
26   unpipe(req)                   //取消在pipe中设置的通道
27 
28   // flush the request
29   onFinished(req, write)    //req流完成之后,触发write事件。
30   req.resume()                 //开启req流
31 }
View Code

注意,express在finalhandler包中重写了流的unpipe的方法,在onfinished包中。

 

express源码剖析(3)

标签:for   cte   没有   standard   cti   his   模板   接下来   disable   

原文地址:http://www.cnblogs.com/liuyinlei/p/6118119.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!