You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
4.2 KiB
225 lines
4.2 KiB
/*! |
|
* express |
|
* Copyright(c) 2009-2013 TJ Holowaychuk |
|
* Copyright(c) 2013 Roman Shtylman |
|
* Copyright(c) 2014-2015 Douglas Christopher Wilson |
|
* MIT Licensed |
|
*/ |
|
|
|
'use strict'; |
|
|
|
/** |
|
* Module dependencies. |
|
* @private |
|
*/ |
|
|
|
var debug = require('debug')('express:router:route'); |
|
var flatten = require('array-flatten'); |
|
var Layer = require('./layer'); |
|
var methods = require('methods'); |
|
|
|
/** |
|
* Module variables. |
|
* @private |
|
*/ |
|
|
|
var slice = Array.prototype.slice; |
|
var toString = Object.prototype.toString; |
|
|
|
/** |
|
* Module exports. |
|
* @public |
|
*/ |
|
|
|
module.exports = Route; |
|
|
|
/** |
|
* Initialize `Route` with the given `path`, |
|
* |
|
* @param {String} path |
|
* @public |
|
*/ |
|
|
|
function Route(path) { |
|
this.path = path; |
|
this.stack = []; |
|
|
|
debug('new %o', path) |
|
|
|
// route handlers for various http methods |
|
this.methods = {}; |
|
} |
|
|
|
/** |
|
* Determine if the route handles a given method. |
|
* @private |
|
*/ |
|
|
|
Route.prototype._handles_method = function _handles_method(method) { |
|
if (this.methods._all) { |
|
return true; |
|
} |
|
|
|
var name = method.toLowerCase(); |
|
|
|
if (name === 'head' && !this.methods['head']) { |
|
name = 'get'; |
|
} |
|
|
|
return Boolean(this.methods[name]); |
|
}; |
|
|
|
/** |
|
* @return {Array} supported HTTP methods |
|
* @private |
|
*/ |
|
|
|
Route.prototype._options = function _options() { |
|
var methods = Object.keys(this.methods); |
|
|
|
// append automatic head |
|
if (this.methods.get && !this.methods.head) { |
|
methods.push('head'); |
|
} |
|
|
|
for (var i = 0; i < methods.length; i++) { |
|
// make upper case |
|
methods[i] = methods[i].toUpperCase(); |
|
} |
|
|
|
return methods; |
|
}; |
|
|
|
/** |
|
* dispatch req, res into this route |
|
* @private |
|
*/ |
|
|
|
Route.prototype.dispatch = function dispatch(req, res, done) { |
|
var idx = 0; |
|
var stack = this.stack; |
|
var sync = 0 |
|
|
|
if (stack.length === 0) { |
|
return done(); |
|
} |
|
|
|
var method = req.method.toLowerCase(); |
|
if (method === 'head' && !this.methods['head']) { |
|
method = 'get'; |
|
} |
|
|
|
req.route = this; |
|
|
|
next(); |
|
|
|
function next(err) { |
|
// signal to exit route |
|
if (err && err === 'route') { |
|
return done(); |
|
} |
|
|
|
// signal to exit router |
|
if (err && err === 'router') { |
|
return done(err) |
|
} |
|
|
|
var layer = stack[idx++]; |
|
if (!layer) { |
|
return done(err); |
|
} |
|
|
|
// max sync stack |
|
if (++sync > 100) { |
|
return setImmediate(next, 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); |
|
} |
|
|
|
sync = 0 |
|
} |
|
}; |
|
|
|
/** |
|
* Add a handler for all HTTP verbs to this route. |
|
* |
|
* Behaves just like middleware and can respond or call `next` |
|
* to continue processing. |
|
* |
|
* You can use multiple `.all` call to add multiple handlers. |
|
* |
|
* function check_something(req, res, next){ |
|
* next(); |
|
* }; |
|
* |
|
* function validate_user(req, res, next){ |
|
* next(); |
|
* }; |
|
* |
|
* route |
|
* .all(validate_user) |
|
* .all(check_something) |
|
* .get(function(req, res, next){ |
|
* res.send('hello world'); |
|
* }); |
|
* |
|
* @param {function} handler |
|
* @return {Route} for chaining |
|
* @api public |
|
*/ |
|
|
|
Route.prototype.all = function all() { |
|
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.all() requires a callback function but got a ' + type |
|
throw new TypeError(msg); |
|
} |
|
|
|
var layer = Layer('/', {}, handle); |
|
layer.method = undefined; |
|
|
|
this.methods._all = true; |
|
this.stack.push(layer); |
|
} |
|
|
|
return this; |
|
}; |
|
|
|
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) |
|
|
|
var layer = Layer('/', {}, handle); |
|
layer.method = method; |
|
|
|
this.methods[method] = true; |
|
this.stack.push(layer); |
|
} |
|
|
|
return this; |
|
}; |
|
});
|
|
|