登录
注册
node.js 学习社区
Connect/Express 中间件具体处理流程以及错误处理机制解密

昊昊晴

2014-12-16 16:50

Express/Connect对于错误处理有个约定,见 http://expressjs.com/guide.html 的Error handling部分,这里约定express的错误处理统一采用如下方式

app.use(function(err, req, res, next){
  console.error(err.stack);
  res.send(500, 'Something broke!');
});

错误处理这段描述大意是这样的:”错误处理中间件必须按规则定义,它必须有4个参数。”
关于错误处理有个示例,在Express项目的example中如下地址:

https://github.com/visionmedia/express/blob/master/examples/web-service/index.js#L52

他如此强调“四个参数"让我我很好奇,中间件的处理流程,错误处理机制是怎样实现,我偏不四个参数会怎样?便忍不住想一探究竟,也就开始了分析。

我们知道express是依赖connect而建立的,整个中间件的处理跟错误处理机制都都是在connect中,因此我就直接扒开了connect的源码,其实说句实话代码不多,但是刚开始阅读的时候还是挺不容易看明白的。看了网上别人的一些文章,分析的也很粗略,如下:

http://www.infoq.com/cn/articles/nodejs-connect-module

只讲了大致原理,可是大致原理大家都懂,我好奇具体流程是怎么实现的,经过反复阅读后,终于把思路整理如下,大家可以对照:

https://github.com/senchalabs/connect/blob/master/lib/proto.js#L101

来看的话效果会更好。(本来做想做流程图的,但是发现流程图画出来更加让人难以理解,我选择用中文做“伪代码”,对于猿猿们应该跟好理解,省略了其他的不重要的代码,比如一些路径的处理代码,这不在我好奇的范围内。)

阅读之前一定要熟悉一下两点:

1、“层”“中间件”是可以理解为相同概念,可以看到所有文档中都是描述为“中间件”,connect当初的设计思路是希望像洋葱那样一层层的运行的,因此在代码用的是“layer”

2、文中“[直接]”表示return了,当前代码不再往下执行了,而是转而执行return后面的内容了。

/**
 * Handle server requests, punting them down
 * the middleware stack.
 *
 * @api private
 */

app.handle = function(req, res, out) {
  var stack = this.stack

  function next(err) {

    [......]

    layer = stack[index++];

    if (/*已经遍历完所有层*/) {

      if (/*有未处理的错误*/) {

        //对于错误的处理(格式化错误信息,打印输出)

      } else {

        //404错误的处理(格式化错误信息,打印输出)

      }
      return;
    }

    try {

      if (/*当前层的路径不匹配*/) { 

           //把当前请求[直接]交给下一个层(中间件)处理

      };

      if (/*存在尚未处理的错误*/) {

        if (/*当前层的handle的参数个数是否为4*/) {

          //如果当前层的handle的参数个数为4的话意味着当前层即错误处理层
          //而如果当层的handle的参数个数不为4的话,则当前层不是错误处理层,则[直接]交给下一层处理
          //如果下一层仍旧不是错误处理层的话将不断往下传,直到交给错误处理层,如果没有任何错误处理层的话,到最后会调用默认的错误处理

          layer.handle(err, req, res, next);

        } else {

          //交给下一层处理
          next(err);

        }
      } else if (/*如果参数个数小于4*/) {

        //当前层的handle的参数个数小于4意味这当前层是正常业务逻辑处理层
        //当没有尚未处理的错误,并且当前当前层是正常业务逻辑处理层,则调用本层的handle处理

        layer.handle(req, res, next);

      } else {

        //如果没有尚未处理的错误信息,但是当前层又不是正常业务逻辑处理层,则[直接]交给下一层处理

        next();
      }
    } catch (e) {

      next(e);

    }
  }

  //启动next()
  next();
};

原文引自:http://cnodejs.org/topic/504c13f6e2b845157707aaf4

回复 · 0

发表回复

你可以在回复中 @ 其他人