route.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. var name = method.toLowerCase();
  50. if (name === 'head' && !this.methods['head']) {
  51. name = 'get';
  52. }
  53. return Boolean(this.methods[name]);
  54. };
  55. /**
  56. * @return {Array} supported HTTP methods
  57. * @private
  58. */
  59. Route.prototype._options = function _options() {
  60. var methods = Object.keys(this.methods);
  61. // append automatic head
  62. if (this.methods.get && !this.methods.head) {
  63. methods.push('head');
  64. }
  65. for (var i = 0; i < methods.length; i++) {
  66. // make upper case
  67. methods[i] = methods[i].toUpperCase();
  68. }
  69. return methods;
  70. };
  71. /**
  72. * dispatch req, res into this route
  73. * @private
  74. */
  75. Route.prototype.dispatch = function dispatch(req, res, done) {
  76. var idx = 0;
  77. var stack = this.stack;
  78. if (stack.length === 0) {
  79. return done();
  80. }
  81. var method = req.method.toLowerCase();
  82. if (method === 'head' && !this.methods['head']) {
  83. method = 'get';
  84. }
  85. req.route = this;
  86. next();
  87. function next(err) {
  88. // signal to exit route
  89. if (err && err === 'route') {
  90. return done();
  91. }
  92. // signal to exit router
  93. if (err && err === 'router') {
  94. return done(err)
  95. }
  96. var layer = stack[idx++];
  97. if (!layer) {
  98. return done(err);
  99. }
  100. if (layer.method && layer.method !== method) {
  101. return next(err);
  102. }
  103. if (err) {
  104. layer.handle_error(err, req, res, next);
  105. } else {
  106. layer.handle_request(req, res, next);
  107. }
  108. }
  109. };
  110. /**
  111. * Add a handler for all HTTP verbs to this route.
  112. *
  113. * Behaves just like middleware and can respond or call `next`
  114. * to continue processing.
  115. *
  116. * You can use multiple `.all` call to add multiple handlers.
  117. *
  118. * function check_something(req, res, next){
  119. * next();
  120. * };
  121. *
  122. * function validate_user(req, res, next){
  123. * next();
  124. * };
  125. *
  126. * route
  127. * .all(validate_user)
  128. * .all(check_something)
  129. * .get(function(req, res, next){
  130. * res.send('hello world');
  131. * });
  132. *
  133. * @param {function} handler
  134. * @return {Route} for chaining
  135. * @api public
  136. */
  137. Route.prototype.all = function all() {
  138. var handles = flatten(slice.call(arguments));
  139. for (var i = 0; i < handles.length; i++) {
  140. var handle = handles[i];
  141. if (typeof handle !== 'function') {
  142. var type = toString.call(handle);
  143. var msg = 'Route.all() requires a callback function but got a ' + type
  144. throw new TypeError(msg);
  145. }
  146. var layer = Layer('/', {}, handle);
  147. layer.method = undefined;
  148. this.methods._all = true;
  149. this.stack.push(layer);
  150. }
  151. return this;
  152. };
  153. methods.forEach(function(method){
  154. Route.prototype[method] = function(){
  155. var handles = flatten(slice.call(arguments));
  156. for (var i = 0; i < handles.length; i++) {
  157. var handle = handles[i];
  158. if (typeof handle !== 'function') {
  159. var type = toString.call(handle);
  160. var msg = 'Route.' + method + '() requires a callback function but got a ' + type
  161. throw new Error(msg);
  162. }
  163. debug('%s %o', method, this.path)
  164. var layer = Layer('/', {}, handle);
  165. layer.method = method;
  166. this.methods[method] = true;
  167. this.stack.push(layer);
  168. }
  169. return this;
  170. };
  171. });