route.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*!
  2. * express
  3. * Copyright(c) 2009-2013 TJ Holowaychuk
  4. * Copyright(c) 2013 Roman Shtylman
  5. * Copyright(c) 2014-2015 Douglas Christopher Wilson
  6. * MIT Licensed
  7. */
  8. 'use strict';
  9. /**
  10. * Module dependencies.
  11. * @private
  12. */
  13. var debug = require('debug')('express:router:route');
  14. var flatten = require('array-flatten');
  15. var Layer = require('./layer');
  16. var methods = require('methods');
  17. /**
  18. * Module variables.
  19. * @private
  20. */
  21. var slice = Array.prototype.slice;
  22. var toString = Object.prototype.toString;
  23. /**
  24. * Module exports.
  25. * @public
  26. */
  27. module.exports = Route;
  28. /**
  29. * Initialize `Route` with the given `path`,
  30. *
  31. * @param {String} path
  32. * @public
  33. */
  34. function Route(path) {
  35. this.path = path;
  36. this.stack = [];
  37. debug('new %o', path)
  38. // route handlers for various http methods
  39. this.methods = {};
  40. }
  41. /**
  42. * Determine if the route handles a given method.
  43. * @private
  44. */
  45. Route.prototype._handles_method = function _handles_method(method) {
  46. if (this.methods._all) {
  47. return true;
  48. }
  49. // normalize name
  50. var name = typeof method === 'string'
  51. ? method.toLowerCase()
  52. : method
  53. if (name === 'head' && !this.methods['head']) {
  54. name = 'get';
  55. }
  56. return Boolean(this.methods[name]);
  57. };
  58. /**
  59. * @return {Array} supported HTTP methods
  60. * @private
  61. */
  62. Route.prototype._options = function _options() {
  63. var methods = Object.keys(this.methods);
  64. // append automatic head
  65. if (this.methods.get && !this.methods.head) {
  66. methods.push('head');
  67. }
  68. for (var i = 0; i < methods.length; i++) {
  69. // make upper case
  70. methods[i] = methods[i].toUpperCase();
  71. }
  72. return methods;
  73. };
  74. /**
  75. * dispatch req, res into this route
  76. * @private
  77. */
  78. Route.prototype.dispatch = function dispatch(req, res, done) {
  79. var idx = 0;
  80. var stack = this.stack;
  81. var sync = 0
  82. if (stack.length === 0) {
  83. return done();
  84. }
  85. var method = typeof req.method === 'string'
  86. ? req.method.toLowerCase()
  87. : req.method
  88. if (method === 'head' && !this.methods['head']) {
  89. method = 'get';
  90. }
  91. req.route = this;
  92. next();
  93. function next(err) {
  94. // signal to exit route
  95. if (err && err === 'route') {
  96. return done();
  97. }
  98. // signal to exit router
  99. if (err && err === 'router') {
  100. return done(err)
  101. }
  102. // max sync stack
  103. if (++sync > 100) {
  104. return setImmediate(next, err)
  105. }
  106. var layer = stack[idx++]
  107. // end of layers
  108. if (!layer) {
  109. return done(err)
  110. }
  111. if (layer.method && layer.method !== method) {
  112. next(err)
  113. } else if (err) {
  114. layer.handle_error(err, req, res, next);
  115. } else {
  116. layer.handle_request(req, res, next);
  117. }
  118. sync = 0
  119. }
  120. };
  121. /**
  122. * Add a handler for all HTTP verbs to this route.
  123. *
  124. * Behaves just like middleware and can respond or call `next`
  125. * to continue processing.
  126. *
  127. * You can use multiple `.all` call to add multiple handlers.
  128. *
  129. * function check_something(req, res, next){
  130. * next();
  131. * };
  132. *
  133. * function validate_user(req, res, next){
  134. * next();
  135. * };
  136. *
  137. * route
  138. * .all(validate_user)
  139. * .all(check_something)
  140. * .get(function(req, res, next){
  141. * res.send('hello world');
  142. * });
  143. *
  144. * @param {function} handler
  145. * @return {Route} for chaining
  146. * @api public
  147. */
  148. Route.prototype.all = function all() {
  149. var handles = flatten(slice.call(arguments));
  150. for (var i = 0; i < handles.length; i++) {
  151. var handle = handles[i];
  152. if (typeof handle !== 'function') {
  153. var type = toString.call(handle);
  154. var msg = 'Route.all() requires a callback function but got a ' + type
  155. throw new TypeError(msg);
  156. }
  157. var layer = Layer('/', {}, handle);
  158. layer.method = undefined;
  159. this.methods._all = true;
  160. this.stack.push(layer);
  161. }
  162. return this;
  163. };
  164. methods.forEach(function(method){
  165. Route.prototype[method] = function(){
  166. var handles = flatten(slice.call(arguments));
  167. for (var i = 0; i < handles.length; i++) {
  168. var handle = handles[i];
  169. if (typeof handle !== 'function') {
  170. var type = toString.call(handle);
  171. var msg = 'Route.' + method + '() requires a callback function but got a ' + type
  172. throw new Error(msg);
  173. }
  174. debug('%s %o', method, this.path)
  175. var layer = Layer('/', {}, handle);
  176. layer.method = method;
  177. this.methods[method] = true;
  178. this.stack.push(layer);
  179. }
  180. return this;
  181. };
  182. });