Connect/Express 中间件具体处理流程以及错误处理机制解密
发布于 2年前 作者 xlqstar 5033 次浏览

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](/user/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();
};
4 回复

写的很好啊,通俗易懂支持下

thank you ,还得多向前辈学习。

这个留着,对接下来的学习有帮助

写的比较清晰。不过重新读第二次的时候感觉多点else 可能逻辑性更强一点。。

回到顶部