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.

210 lines
3.9 KiB

  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 %s', 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. if (err && err === 'route') {
  89. return done();
  90. }
  91. var layer = stack[idx++];
  92. if (!layer) {
  93. return done(err);
  94. }
  95. if (layer.method && layer.method !== method) {
  96. return next(err);
  97. }
  98. if (err) {
  99. layer.handle_error(err, req, res, next);
  100. } else {
  101. layer.handle_request(req, res, next);
  102. }
  103. }
  104. };
  105. /**
  106. * Add a handler for all HTTP verbs to this route.
  107. *
  108. * Behaves just like middleware and can respond or call `next`
  109. * to continue processing.
  110. *
  111. * You can use multiple `.all` call to add multiple handlers.
  112. *
  113. * function check_something(req, res, next){
  114. * next();
  115. * };
  116. *
  117. * function validate_user(req, res, next){
  118. * next();
  119. * };
  120. *
  121. * route
  122. * .all(validate_user)
  123. * .all(check_something)
  124. * .get(function(req, res, next){
  125. * res.send('hello world');
  126. * });
  127. *
  128. * @param {function} handler
  129. * @return {Route} for chaining
  130. * @api public
  131. */
  132. Route.prototype.all = function all() {
  133. var handles = flatten(slice.call(arguments));
  134. for (var i = 0; i < handles.length; i++) {
  135. var handle = handles[i];
  136. if (typeof handle !== 'function') {
  137. var type = toString.call(handle);
  138. var msg = 'Route.all() requires callback functions but got a ' + type;
  139. throw new TypeError(msg);
  140. }
  141. var layer = Layer('/', {}, handle);
  142. layer.method = undefined;
  143. this.methods._all = true;
  144. this.stack.push(layer);
  145. }
  146. return this;
  147. };
  148. methods.forEach(function(method){
  149. Route.prototype[method] = function(){
  150. var handles = flatten(slice.call(arguments));
  151. for (var i = 0; i < handles.length; i++) {
  152. var handle = handles[i];
  153. if (typeof handle !== 'function') {
  154. var type = toString.call(handle);
  155. var msg = 'Route.' + method + '() requires callback functions but got a ' + type;
  156. throw new Error(msg);
  157. }
  158. debug('%s %s', method, this.path);
  159. var layer = Layer('/', {}, handle);
  160. layer.method = method;
  161. this.methods[method] = true;
  162. this.stack.push(layer);
  163. }
  164. return this;
  165. };
  166. });