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