/**
* Module dependencies.
*/
var pathRegexp = require('path-to-regexp');
var debug = require('debug')('express:router:layer');
/**
* Module variables.
*/
var hasOwnProperty = Object.prototype.hasOwnProperty;
其中path-to-regexp
模块的作用是将一个Express的表示路径的字符串转换成一个正则表达式。例如:
var pathToRegexp = require('path-to-regexp');
var keys = [];
var re = pathToRegexp('/foo/:bar', keys);
// re = /^\/foo\/([^\/]+?)\/?$/i
// keys = [{
// name: 'bar',
// prefix: '/',
// delimiter: '/',
// optional: false,
// repeat: false,
// pattern: '[^\\/]+?'
// }]
re.exec('/foo/test');
// => ['/foo/test', 'test']
该模块的导出对象为Layer
类,源码如下:
/**
* Expose `Layer`.
*/
module.exports = Layer;
function Layer(path, options, fn) {
if (!(this instanceof Layer)) {
return new Layer(path, options, fn);
}
debug('new %s', path);
options = options || {};
this.handle = fn;
this.name = fn.name || '<anonymous>';
this.params = undefined;
this.path = undefined;
this.regexp = pathRegexp(path, this.keys = [], options);
if (path === '/' && options.end === false) {
this.regexp.fast_slash = true;
}
}
其中主要是对一些实例属性的设置,主要包括:
handle
name
params
path
regexp
keys
然后当path
为/
且options.end
为false
的时候,会设置this.regexp.fast_slash
的值为true
。Layer
的创建主要是在Route
和Router
中。
- 在
Route
模块中,当调用all()
或METHOD()
方法的时候,会创建Layer
,但是创建方式为Layer('/', {}, fn)
,及第二个参数为一个空对象,因此此时的Layer
实例并未进行regexp.fast_slash
设置。 - 在
Router
模块中,当调用use()
或route()
方法的时候,会创建Layer()
use()
:options.end
为false
,因此当path
为/
时,fast_slash
为true
route()
:options.end
为true
,因此不设置fast_slash
因此,只有当path
为/
且该中间件不是路由中间件的时候,才会设置this.regexp.fast_slash
的值为true
。
该方法用来判断该Layer
对象是否与给定的path
相匹配,并进行后续处理。源码如下:
/**
* Check if this route matches `path`, if so
* populate `.params`.
*
* @param {String} path
* @return {Boolean}
* @api private
*/
Layer.prototype.match = function match(path) {
if (path == null) {
// no path, nothing matches
this.params = undefined;
this.path = undefined;
return false;
}
if (this.regexp.fast_slash) {
// fast path non-ending match for / (everything matches)
this.params = {};
this.path = '';
return true;
}
var m = this.regexp.exec(path);
if (!m) {
this.params = undefined;
this.path = undefined;
return false;
}
// store values
this.params = {};
this.path = m[0];
var keys = this.keys;
var params = this.params;
var prop;
var n = 0;
var key;
var val;
for (var i = 1, len = m.length; i < len; ++i) {
key = keys[i - 1];
prop = key
? key.name
: n++;
val = decode_param(m[i]);
if (val !== undefined || !(hasOwnProperty.call(params, prop))) {
params[prop] = val;
}
}
return true;
};
思路如下:
- 如果
path
无效,则设置this.params
和this.path
为undefined
,返回false
- 如果
this.regexp.fast_slash
为true
,则设置this.params
为空对象,设置this.path
为空字符串,返回true
- 执行
this.regexp.exec(path)
判断是否匹配,如果不匹配,则设置this.params
和this.path
为undefined
,返回false
- 如果匹配,设置
this.path
为m[0]
,并对this.params
进行赋值,然后返回true
该方法用来处理HTTP请求,源码如下:
/**
* Handle the request for the layer.
*
* @param {Request} req
* @param {Response} res
* @param {function} next
* @api private
*/
Layer.prototype.handle_request = function handle(req, res, next) {
var fn = this.handle;
if (fn.length > 3) {
// not a standard request handler
return next();
}
try {
fn(req, res, next);
} catch (err) {
next(err);
}
};
如果处理函数的参数个数大于3,则认为不是一个标准的处理函数,因此不执行,而是直接执行next()
;否则执行fn
。
例如下面的例子,处理函数就不会执行:
app.use('/', function(req, res, next, foo) {
console.log('hello world');
next();
});
对于Router
中存放的Layer
,如果是普通中间件的话,调用的就是使用app.use()
时注册的处理函数;如果是路由中间件的话,调用的是route.dispatch()
方法,相关处理函数的注册在Router
的route()
方法中,代码如下:
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: this.strict,
end: true
}, route.dispatch.bind(route));
handle_error
方法与handle_request
的逻辑基本类似,不做赘述。