index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. /**
  2. * Module dependencies.
  3. */
  4. var Route = require('./route');
  5. var Layer = require('./layer');
  6. var methods = require('methods');
  7. var debug = require('debug')('express:router');
  8. var parseUrl = require('parseurl');
  9. var slice = Array.prototype.slice;
  10. /**
  11. * Initialize a new `Router` with the given `options`.
  12. *
  13. * @param {Object} options
  14. * @return {Router} which is an callable function
  15. * @api public
  16. */
  17. var proto = module.exports = function(options) {
  18. options = options || {};
  19. function router(req, res, next) {
  20. router.handle(req, res, next);
  21. }
  22. // mixin Router class functions
  23. router.__proto__ = proto;
  24. router.params = {};
  25. router._params = [];
  26. router.caseSensitive = options.caseSensitive;
  27. router.strict = options.strict;
  28. router.stack = [];
  29. return router;
  30. };
  31. /**
  32. * Map the given param placeholder `name`(s) to the given callback.
  33. *
  34. * Parameter mapping is used to provide pre-conditions to routes
  35. * which use normalized placeholders. For example a _:user_id_ parameter
  36. * could automatically load a user's information from the database without
  37. * any additional code,
  38. *
  39. * The callback uses the same signature as middleware, the only difference
  40. * being that the value of the placeholder is passed, in this case the _id_
  41. * of the user. Once the `next()` function is invoked, just like middleware
  42. * it will continue on to execute the route, or subsequent parameter functions.
  43. *
  44. * Just like in middleware, you must either respond to the request or call next
  45. * to avoid stalling the request.
  46. *
  47. * app.param('user_id', function(req, res, next, id){
  48. * User.find(id, function(err, user){
  49. * if (err) {
  50. * return next(err);
  51. * } else if (!user) {
  52. * return next(new Error('failed to load user'));
  53. * }
  54. * req.user = user;
  55. * next();
  56. * });
  57. * });
  58. *
  59. * @param {String} name
  60. * @param {Function} fn
  61. * @return {app} for chaining
  62. * @api public
  63. */
  64. proto.param = function(name, fn){
  65. // param logic
  66. if ('function' == typeof name) {
  67. this._params.push(name);
  68. return;
  69. }
  70. // apply param functions
  71. var params = this._params;
  72. var len = params.length;
  73. var ret;
  74. if (name[0] === ':') {
  75. name = name.substr(1);
  76. }
  77. for (var i = 0; i < len; ++i) {
  78. if (ret = params[i](name, fn)) {
  79. fn = ret;
  80. }
  81. }
  82. // ensure we end up with a
  83. // middleware function
  84. if ('function' != typeof fn) {
  85. throw new Error('invalid param() call for ' + name + ', got ' + fn);
  86. }
  87. (this.params[name] = this.params[name] || []).push(fn);
  88. return this;
  89. };
  90. /**
  91. * Dispatch a req, res into the router.
  92. *
  93. * @api private
  94. */
  95. proto.handle = function(req, res, done) {
  96. var self = this;
  97. debug('dispatching %s %s', req.method, req.url);
  98. var method = req.method.toLowerCase();
  99. var search = 1 + req.url.indexOf('?');
  100. var pathlength = search ? search - 1 : req.url.length;
  101. var fqdn = 1 + req.url.substr(0, pathlength).indexOf('://');
  102. var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
  103. var idx = 0;
  104. var removed = '';
  105. var slashAdded = false;
  106. var paramcalled = {};
  107. // store options for OPTIONS request
  108. // only used if OPTIONS request
  109. var options = [];
  110. // middleware and routes
  111. var stack = self.stack;
  112. // manage inter-router variables
  113. var parent = req.next;
  114. var parentUrl = req.baseUrl || '';
  115. done = wrap(done, function(old, err) {
  116. req.baseUrl = parentUrl;
  117. req.next = parent;
  118. old(err);
  119. });
  120. req.next = next;
  121. // for options requests, respond with a default if nothing else responds
  122. if (method === 'options') {
  123. done = wrap(done, function(old, err) {
  124. if (err || options.length === 0) return old(err);
  125. var body = options.join(',');
  126. return res.set('Allow', body).send(body);
  127. });
  128. }
  129. next();
  130. function next(err) {
  131. if (err === 'route') {
  132. err = undefined;
  133. }
  134. var layer = stack[idx++];
  135. var layerPath;
  136. if (!layer) {
  137. return done(err);
  138. }
  139. if (slashAdded) {
  140. req.url = req.url.substr(1);
  141. slashAdded = false;
  142. }
  143. req.baseUrl = parentUrl;
  144. req.url = protohost + removed + req.url.substr(protohost.length);
  145. req.originalUrl = req.originalUrl || req.url;
  146. removed = '';
  147. try {
  148. var path = parseUrl(req).pathname;
  149. if (undefined == path) path = '/';
  150. if (!layer.match(path)) return next(err);
  151. // route object and not middleware
  152. var route = layer.route;
  153. // if final route, then we support options
  154. if (route) {
  155. // we don't run any routes with error first
  156. if (err) {
  157. return next(err);
  158. }
  159. req.route = route;
  160. // we can now dispatch to the route
  161. if (method === 'options' && !route.methods['options']) {
  162. options.push.apply(options, route._options());
  163. }
  164. }
  165. // Capture one-time layer values
  166. req.params = layer.params;
  167. layerPath = layer.path;
  168. // this should be done for the layer
  169. return self.process_params(layer, paramcalled, req, res, function(err) {
  170. if (err) {
  171. return next(err);
  172. }
  173. if (route) {
  174. return layer.handle(req, res, next);
  175. }
  176. trim_prefix();
  177. });
  178. } catch (err) {
  179. next(err);
  180. }
  181. function trim_prefix() {
  182. var c = path[layerPath.length];
  183. if (c && '/' != c && '.' != c) return next(err);
  184. // Trim off the part of the url that matches the route
  185. // middleware (.use stuff) needs to have the path stripped
  186. removed = layerPath;
  187. if (removed.length) {
  188. debug('trim prefix (%s) from url %s', layerPath, req.url);
  189. req.url = protohost + req.url.substr(protohost.length + removed.length);
  190. }
  191. // Ensure leading slash
  192. if (!fqdn && req.url[0] !== '/') {
  193. req.url = '/' + req.url;
  194. slashAdded = true;
  195. }
  196. // Setup base URL (no trailing slash)
  197. if (removed.length && removed.substr(-1) === '/') {
  198. req.baseUrl = parentUrl + removed.substring(0, removed.length - 1);
  199. } else {
  200. req.baseUrl = parentUrl + removed;
  201. }
  202. debug('%s %s : %s', layer.handle.name || 'anonymous', layerPath, req.originalUrl);
  203. var arity = layer.handle.length;
  204. try {
  205. if (err && arity === 4) {
  206. layer.handle(err, req, res, next);
  207. } else if (!err && arity < 4) {
  208. layer.handle(req, res, next);
  209. } else {
  210. next(err);
  211. }
  212. } catch (err) {
  213. next(err);
  214. }
  215. }
  216. }
  217. function wrap(old, fn) {
  218. return function () {
  219. var args = [old].concat(slice.call(arguments));
  220. fn.apply(this, args);
  221. };
  222. }
  223. };
  224. /**
  225. * Process any parameters for the layer.
  226. *
  227. * @api private
  228. */
  229. proto.process_params = function(layer, called, req, res, done) {
  230. var params = this.params;
  231. // captured parameters from the layer, keys and values
  232. var keys = layer.keys;
  233. // fast track
  234. if (!keys || keys.length === 0) {
  235. return done();
  236. }
  237. var i = 0;
  238. var name;
  239. var paramIndex = 0;
  240. var key;
  241. var paramVal;
  242. var paramCallbacks;
  243. var paramCalled;
  244. // process params in order
  245. // param callbacks can be async
  246. function param(err) {
  247. if (err) {
  248. return done(err);
  249. }
  250. if (i >= keys.length ) {
  251. return done();
  252. }
  253. paramIndex = 0;
  254. key = keys[i++];
  255. if (!key) {
  256. return done();
  257. }
  258. name = key.name;
  259. paramVal = req.params[name];
  260. paramCallbacks = params[name];
  261. paramCalled = called[name];
  262. if (paramVal === undefined || !paramCallbacks) {
  263. return param();
  264. }
  265. // param previously called with same value or error occurred
  266. if (paramCalled && (paramCalled.error || paramCalled.match === paramVal)) {
  267. // restore value
  268. req.params[name] = paramCalled.value;
  269. // next param
  270. return param(paramCalled.error);
  271. }
  272. called[name] = paramCalled = {
  273. error: null,
  274. match: paramVal,
  275. value: paramVal
  276. };
  277. try {
  278. return paramCallback();
  279. } catch (err) {
  280. return done(err);
  281. }
  282. }
  283. // single param callbacks
  284. function paramCallback(err) {
  285. var fn = paramCallbacks[paramIndex++];
  286. // store updated value
  287. paramCalled.value = req.params[key.name];
  288. if (err) {
  289. // store error
  290. paramCalled.error = err;
  291. param(err);
  292. return;
  293. }
  294. if (!fn) return param();
  295. fn(req, res, paramCallback, paramVal, key.name);
  296. }
  297. param();
  298. };
  299. /**
  300. * Use the given middleware function, with optional path, defaulting to "/".
  301. *
  302. * Use (like `.all`) will run for any http METHOD, but it will not add
  303. * handlers for those methods so OPTIONS requests will not consider `.use`
  304. * functions even if they could respond.
  305. *
  306. * The other difference is that _route_ path is stripped and not visible
  307. * to the handler function. The main effect of this feature is that mounted
  308. * handlers can operate without any code changes regardless of the "prefix"
  309. * pathname.
  310. *
  311. * @param {String|Function} route
  312. * @param {Function} fn
  313. * @return {app} for chaining
  314. * @api public
  315. */
  316. proto.use = function(route, fn){
  317. // default route to '/'
  318. if ('string' != typeof route) {
  319. fn = route;
  320. route = '/';
  321. }
  322. if (typeof fn !== 'function') {
  323. var type = {}.toString.call(fn);
  324. var msg = 'Router.use() requires callback functions but got a ' + type;
  325. throw new Error(msg);
  326. }
  327. // strip trailing slash
  328. if ('/' == route[route.length - 1]) {
  329. route = route.slice(0, -1);
  330. }
  331. var layer = new Layer(route, {
  332. sensitive: this.caseSensitive,
  333. strict: this.strict,
  334. end: false
  335. }, fn);
  336. // add the middleware
  337. debug('use %s %s', route || '/', fn.name || 'anonymous');
  338. this.stack.push(layer);
  339. return this;
  340. };
  341. /**
  342. * Create a new Route for the given path.
  343. *
  344. * Each route contains a separate middleware stack and VERB handlers.
  345. *
  346. * See the Route api documentation for details on adding handlers
  347. * and middleware to routes.
  348. *
  349. * @param {String} path
  350. * @return {Route}
  351. * @api public
  352. */
  353. proto.route = function(path){
  354. var route = new Route(path);
  355. var layer = new Layer(path, {
  356. sensitive: this.caseSensitive,
  357. strict: this.strict,
  358. end: true
  359. }, route.dispatch.bind(route));
  360. layer.route = route;
  361. this.stack.push(layer);
  362. return route;
  363. };
  364. // create Router#VERB functions
  365. methods.concat('all').forEach(function(method){
  366. proto[method] = function(path){
  367. var route = this.route(path)
  368. route[method].apply(route, [].slice.call(arguments, 1));
  369. return this;
  370. };
  371. });