注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

snoopyxdy的博客

https://github.com/DoubleSpout

 
 
 

日志

 
 

expressjs源码解读(三) —— router.js  

2011-12-16 16:43:40|  分类: node |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
connect.js中middleware最常用的也就属router.js了,expressjs对这个模块进行了改写,更加简洁,我们先看connect.js中的router.js是如何实现路由功能的。
同样,route.js将route函数exports出去了:
exports = module.exports = router;
定义了一个数组:
var _methods = exports.methods = [
  'get'
  , 'post'
  , 'put'
  , 'delete'
  , 'connect'
  , 'options'
...
]
这个数组是存放http请求类型的,常用的也就4种:get、post、put、delete。

1、大route函数
为什么说这里是大router函数呢?因为大router函数里还有个小router函数,是大router函数的return值。大router函数期望接收一个函数作为参数。我们看示例:
  connect.router(function(app){
 *       app.get('/user/:id', function(req, res, next){
 *         // populates req.params.id
 *       });
 *       app.put('/user/:id', function(req, res, next){
 *         // populates req.params.id
 *       });
 *     })
大router函数先将_methods数组中的每个值添加到methods对象中,生成统一格式的方法
  _methods.forEach(function(method){
    methods[method] = generateMethodFunction(method.toUpperCase());
  });

2、generateMethodFunction 函数
真个router.js比较重要的函数,我们来看 generateMethodFunction 这个函数:
1、var localRoutes = routes[name] = routes[name] || [];
2、 localRoutes.push({
          fn: fn
        , path: regexp
        , keys: keys
        , orig: path
        , method: name
      });
将routes[name] 定义为数组,也就是routes对象的定义key为_method数组中的每个值,然后这个routes[method] 定义为数组,对所有一类的比如get方法的route设定都丢入到routes['get']数组中去。

3、小router函数
在大ruouter函数内部有一个同名的router函数,我们称之为小router函数,他是大router函数的返回值,也就是当有request请求进来时,会从stack堆栈出列并被调用的函数。
function router(req, res, next){ ... }
期待接收3个参数,不多说,你懂的。
在小router函数内部有一个名为pass的函数,我们看下它有什么作用:
1、 if (route = match(req, routes, i)) { ... }
调用match方法,并将match的返回值赋值给route变量。
我们来看看match到底是做什么的:
  if ('HEAD' == method) method = 'GET';
  if (routes = routes[method]) { ... }
如果是head请求,则将method设置为get,如果能够在routes对象中找到指定的key,则继续,否则返回undefined。
var url = parse(req.url)
      , pathname = url.pathname;
 for (var len = routes.length; i < len; ++i) { ... }
对 routes[method] 数组循环,这个数组也就是我们刚才在 generateMethodFunction  函数push进去的东西,是根据method分类的。
 var route = routes[i]
        , fn = route.fn
        , path = route.path
        , keys = fn.keys = route.keys;
 if (captures = path.exec(pathname)) { ... }
如果访问的url和routes[method]数组中的path正则匹配,则执行。
  for (var j = 1, len = captures.length; j < len; ++j) {
          var key = keys[j-1],
            val = typeof captures[j] === 'string'
              ? decodeURIComponent(captures[j])
              : captures[j];
          if (key) {
            fn.params[key] = val;
          } else {
            fn.params.push(val);
          }
        }
用正则判断出所带的url参数并且切割url。
最后:
req._route_index = i;
return fn;
将第几个匹配的 index 赋值给 req对象。
这样return的fn将是带有 params数组对象的函数
注意上面橙色部分:
fn.params是一个数组,调用push方法就可以往数组队列最后插入内容,然而对于javascript一切皆对象,所以我们还可以定义 fn.params[key] = val;我们可以使用fn.params[0]这样来读取数组内容,利用fn.params.id来读取对象内的值。
firebug运行代码:
var x = [];
x.push(123);
x.push(222);
x.yyy = 111;
alert(x[1]);
alert(x['yyy']);
console.log(x);
然后是两个函数pass(i)和param(err),具体实现写的太TM乱了,没看明白,而且注释也相当简单,这里应该是可以重构的地方,目前了解知道这2个函数的递归调用可以让routes['get']数组中的每一项都去判断,如果匹配则分别执行回调route:
route.call(self, req, res, function(err){
                    if (err) {
                      next(err);
                    } else {
                      pass(req._route_index + 1);
                    }
                  });
最后是判断option请求:
else if ('OPTIONS' == req.method)  options(req, res, routes);
else next();
同时还为小router函数添加了一些属性和方法,比如:router.match 用来获得指定url匹配指定方法的回调函数。

3、小结
个人感觉connect.js的这个router.js模块写相当的一般,实现过于复杂,而且在它新版本的connect.js没找到router.js文件,可能作者也意识到了这一点,下一篇我去看一下express.js的router模块,看看重新改写了的connect.js的router.js。






  评论这张
 
阅读(2400)| 评论(2)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2016