225 lines
4.2 KiB
JavaScript
225 lines
4.2 KiB
JavaScript
/*!
|
|
* 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)
|
|
}
|
|
|
|
// max sync stack
|
|
if (++sync > 100) {
|
|
return setImmediate(next, err)
|
|
}
|
|
|
|
var layer = stack[idx++]
|
|
|
|
// end of layers
|
|
if (!layer) {
|
|
return done(err)
|
|
}
|
|
|
|
if (layer.method && layer.method !== method) {
|
|
next(err)
|
|
} else 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;
|
|
};
|
|
});
|