Skip to content
This repository has been archived by the owner on Oct 31, 2022. It is now read-only.

Latest commit

 

History

History
212 lines (164 loc) · 5.12 KB

File metadata and controls

212 lines (164 loc) · 5.12 KB

layer.js

1. 模块依赖及变量定义

/**
 * 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']

2. 导出对象

该模块的导出对象为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.endfalse的时候,会设置this.regexp.fast_slash的值为trueLayer的创建主要是在RouteRouter中。

  • Route模块中,当调用all()METHOD()方法的时候,会创建Layer,但是创建方式为Layer('/', {}, fn),及第二个参数为一个空对象,因此此时的Layer实例并未进行regexp.fast_slash设置。
  • Router模块中,当调用use()route()方法的时候,会创建Layer()
    • use()options.endfalse,因此当path/时,fast_slashtrue
    • route()options.endtrue,因此不设置fast_slash

因此,只有当path/且该中间件不是路由中间件的时候,才会设置this.regexp.fast_slash的值为true

3. match

该方法用来判断该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.paramsthis.pathundefined,返回false
  • 如果this.regexp.fast_slashtrue,则设置this.params为空对象,设置this.path为空字符串,返回true
  • 执行this.regexp.exec(path)判断是否匹配,如果不匹配,则设置this.paramsthis.pathundefined,返回false
  • 如果匹配,设置this.pathm[0],并对this.params进行赋值,然后返回true

4. handle_requesthandle_error

该方法用来处理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()方法,相关处理函数的注册在Routerroute()方法中,代码如下:

var layer = new Layer(path, {
  sensitive: this.caseSensitive,
  strict: this.strict,
  end: true
}, route.dispatch.bind(route));

handle_error方法与handle_request的逻辑基本类似,不做赘述。