之前的讨论都局限于use方法,所有方式的请求会被通过,这一节讨论express内部如何处理特殊请求方法。

  给个流程图咯~

  分别给出app.METHODS与router.METHODS:

// app.use
methods.forEach(function(method) {
// app.get、app.post...
app[method] = function(path) {
if (method === 'get' && arguments.length === 1) {
// app.get(setting)
return this.set(path);
}
// 这里过
this.lazyrouter();
// 生成一个route对象
var route = this._router.route(path);
// 调用route的方法
route[method].apply(route, slice.call(arguments, 1));
return this;
};
});
// router.use
methods.concat('all').forEach(function(method) {
proto[method] = function(path) {
var route = this.route(path)
route[method].apply(route, slice.call(arguments, 1));
return this;
};
});

  大体上都是一样的,唯一奇怪的是在app模块里,单独定义了app.all方法,虽然内容只是遍历METHODS数组调用对应的方法,但是这比起直接让route处理不是更优雅么(跟开发者提了这个问题,得到了答复,超开心!)……

router.route

  上述的两个方法都先指向了router模块的route方法,源码如下:

proto.route = function route(path) {
// new一个Route对象
var route = new Route(path);
// new一个Layer对象
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: this.strict,
end: true
}, route.dispatch.bind(route));
// 这种形式的Layer有route属性
layer.route = route;
// 同样push到stack中
this.stack.push(layer);
return route;
};

  这里接连生成了一个route对象与一个layer对象,由于只传进来了path,所以layer的中间件变成了一个route内置方法,暂时不管。

Route

  这里看一眼route对象的构造函数:

function Route(path) {
this.path = path;
this.stack = []; debug('new %o', path) // 不同请求方式的方法集合
this.methods = {};
}

  非常简单,头疼的是每个route也有一个stack。

  在返回route实例后后,随即调用route对应的method,并传入中间件函数。

  这里关于app[METHODS](function...)有一个问题,常规情况下app.use直接传函数相当于对所有路径都匹配该中间件,但是如果指定了请求方法后直接传函数,这个代码是无效的,虽然不会报错而且非常顺利的走完流程,但是最后返回一个无用的route对象,4月18号晚上给开发者又发了一封邮件询问这个问题,截止19号早上还没答复我。

route[method]

  先不管这么多,总之先按正常流程走,route[method]源码如下:

methods.forEach(function(method) {
Route.prototype[method] = function() {
// 扁平化参数
var handles = flatten(slice.call(arguments));
// 遍历中间件
for (var i = 0; i < handles.length; i++) {
var handle = handles[i];
// 竟然还有错误检测
if (typeof handle !== 'function') {
var type = toString.call(handle);
var msg = 'Route.' + method + '() requires a callback function but got a ' + type
throw new Error(msg);
} debug('%s %o', method, this.path);
// 方法层级的Layer对象 对路径不关心
var layer = Layer('/', {}, handle);
// 多的一个属性
layer.method = method;
// 标记对象
this.methods[method] = true;
// 这是route的stack
this.stack.push(layer);
} return this;
};
});

  这里的步骤需要稍做梳理:

1、app[method]/router[method]方法最终指向router的route方法

2、router.route会根据path生成一个route对象与一个Layer对象,将route作为一个属性挂载到layer上面,而layer对象会被push进router的stack数组

3、调用route对应的method方法,方法会遍历传入的中间件函数,每一个中间件生成一个无视路径的layer对象,并且layer有一个method属性,最后将layer对象push进route对象。

route.dispatch

  总之流程大概梳理完了,接下来最后补充一下router对象上layer对象的handle函数:route.dispatch

Route.prototype.dispatch = function dispatch(req, res, done) {
var idx = 0;
var stack = this.stack;
// 没有
if (stack.length === 0) {
return done();
}
// 获取请求的方法
var method = req.method.toLowerCase();
// 未注册head方式监听 head请求视为get
if (method === 'head' && !this.methods['head']) {
method = 'get';
} req.route = this; next(); function next(err) {
if (err && err === 'route') return done();
if (err && err === 'router') return done(err);
// 取出对应的中间件
var layer = stack[idx++];
if (!layer) return done(err);
if (layer.method && layer.method !== method) return next(err);
if (err) {
layer.handle_error(err, req, res, next);
} else {
// 真正的处理方法
layer.handle_request(req, res, next);
}
}
};

  源码中的stack为layer数组,当有请求时,每次都会从中取出一个,然后匹配请求方式与layer.method是否一致,最后调用中间件处理请求。

  完事~

.7-浅析express源码之Router模块(3)-app[METHODS]的更多相关文章

  1. .5-浅析express源码之Router模块(1)-默认中间件

    模块application已经完结,开始讲Router路由部分. 切入口仍然在application模块中,方法就是那个随处可见的lazyrouter. 基本上除了初始化init方法,其余的app.u ...

  2. .6-浅析express源码之Router模块(2)-router.use

    这一节继续深入Router模块,首先从最常用的use开始. router.use 方法源码如下: proto.use = function use(fn) { var offset = 0; var ...

  3. express源码剖析--Router模块

    1.加载模块执行代码: methods.forEach(function(method){ //method是http协议的各种请求方法,如:get,put,headee,post Route.pro ...

  4. .3-浅析express源码之applicaiton模块(2)-app.render

    这个模块还漏了一个稍微复杂点的API,就是app.render,首先看官网的定义: app.render(view, [locals], callback) view为对应的文件名,locals为一个 ...

  5. .2-浅析express源码之applicaiton模块(1)-咸鱼方法

    上一节讲了express的入口文件,当执行主函数,会调用app.init方法,这个方法就来源于application模块. 这个模块有很多方法,目前仅仅过一下初始化方法: app.init = fun ...

  6. .4-浅析express源码之applicaiton模块(3)-compile函数

    基本上application模块的api都看的差不多了,但是在app.set中还有一个遗漏点,如下: app.set = function set(setting, val) { // ...设值 / ...

  7. express源码分析之Router

    express作为nodejs平台下非常流行的web框架,相信大家都对其已经很熟悉了,对于express的使用这里不再多说,如有需要可以移步到www.expressjs.com自行查看express的 ...

  8. nginx源码分析之模块初始化

    在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...

  9. 读Zepto源码之Event模块

    Event 模块是 Zepto 必备的模块之一,由于对 Event Api 不太熟,Event 对象也比较复杂,所以乍一看 Event 模块的源码,有点懵,细看下去,其实也不太复杂. 读Zepto源码 ...

随机推荐

  1. cxgrid动态生成footersummary 并获得值

    cxgrid动态生成footersummary 并获得值   var f: TcxGridDBTableSummaryItem; cx_for_mctv.OptionsView.Footer := t ...

  2. 设计模式:visitor

    拜访者模式(visitor)适用于对复杂结构体进行解析的场景. 所谓复杂结构体,是指包含多个子元素的对象,比如集合,树,图,或者组合对象--.结构体中的每个元素,包括结构体本身实现接口: Elemen ...

  3. C#基础之访问修饰符

    C#访问修饰符,无时无刻都在使用,这里记录一下,如果写错的地方,欢迎指正. public :公有的,任何代码均可以访问,应用于所有类或成员: internal:内部的,只能在当前程序集中使用,应用于所 ...

  4. Git清空历史,清空历史删除的文件,降低.git 文件大小

    执行以下步骤之前 请做好源码备份 本操作用来清理github上面的历史删除文件,减少库的体积. 第一步骤 下载JDK环境和JAR包 https://rtyley.github.io/bfg-repo- ...

  5. [leetcode.com]算法题目 - Remove Duplicates from Sorted List

    Given a sorted linked list, delete all duplicates such that each element appear only once. For examp ...

  6. delphi char数组、string和Pchar的相互转换

    因为要调用windows的api或者给vc++写接口,很多地方都要用到pchar,现在将char数组.string和pchar之间的相互转换都列出来,都是网上找的资料,我总结一下,先直接上代码,再讲原 ...

  7. JQuery 知识

    1.修改标签内容: *html( )  相当于innerHTML * text(  )  相当于innerText 2.属性操作: *attr(  )  读/写/添加/设置属性 *removeAttr ...

  8. 「PKUWC2019」拓扑序计数(状压dp)

    考场只打了 \(52\) 分暴力...\(ljc\) 跟我说了一下大致思路,我回去敲了敲. \(f[i]\) 表示状态为 \(i\) 时的方案数.我们用二进制 \(0/1\) 表示不选/选点 \(i\ ...

  9. flask~数据库

    flask与数据库的连接基于flaks_sqlaichemy 扩展 首先要连接数据库的时候必须得先下载 pip install flask-sqlalchemy 这个扩展 flask框架与数据库的连接 ...

  10. 创建python3虚拟环境指令和冻结所安装的包

    mkvirtualenv file_name -p python3 去掉后面的 P 和python3 代表创建python2环境 其中p代表路径的意思 冻结所安装包命令 pip freeze > ...