5 var Route
= require('./route')
6 , Layer
= require('./layer')
7 , methods
= require('methods')
8 , debug
= require('debug')('express:router')
9 , parseUrl
= require('parseurl');
12 * Initialize a new `Router` with the given `options`.
14 * @param {Object} options
15 * @return {Router} which is an callable function
19 var proto
= module
.exports = function(options
) {
20 options
= options
|| {};
22 function router(req
, res
, next
) {
23 router
.handle(req
, res
, next
);
26 // mixin Router class functions
27 router
.__proto__
= proto
;
31 router
.caseSensitive
= options
.caseSensitive
;
32 router
.strict
= options
.strict
;
39 * Map the given param placeholder `name`(s) to the given callback.
41 * Parameter mapping is used to provide pre-conditions to routes
42 * which use normalized placeholders. For example a _:user_id_ parameter
43 * could automatically load a user's information from the database without
44 * any additional code,
46 * The callback uses the same signature as middleware, the only difference
47 * being that the value of the placeholder is passed, in this case the _id_
48 * of the user. Once the `next()` function is invoked, just like middleware
49 * it will continue on to execute the route, or subsequent parameter functions.
51 * Just like in middleware, you must either respond to the request or call next
52 * to avoid stalling the request.
54 * app.param('user_id', function(req, res, next, id){
55 * User.find(id, function(err, user){
59 * return next(new Error('failed to load user'));
66 * @param {String} name
67 * @param {Function} fn
68 * @return {app} for chaining
72 proto
.param = function(name
, fn
){
74 if ('function' == typeof name
) {
75 this._params
.push(name
);
79 // apply param functions
80 var params
= this._params
84 if (name
[0] === ':') {
85 name
= name
.substr(1);
88 for (var i
= 0; i
< len
; ++i
) {
89 if (ret
= params
[i
](name
, fn
)) {
94 // ensure we end up with a
95 // middleware function
96 if ('function' != typeof fn
) {
97 throw new Error('invalid param() call for ' + name
+ ', got ' + fn
);
100 (this.params
[name
] = this.params
[name
] || []).push(fn
);
105 * Dispatch a req, res into the router.
110 proto
.handle = function(req
, res
, done
) {
113 debug('dispatching %s %s', req
.method
, req
.url
);
115 var method
= req
.method
.toLowerCase();
117 var search
= 1 + req
.url
.indexOf('?');
118 var pathlength
= search
? search
- 1 : req
.url
.length
;
119 var fqdn
= 1 + req
.url
.substr(0, pathlength
).indexOf('://');
120 var protohost
= fqdn
? req
.url
.substr(0, req
.url
.indexOf('/', 2 + fqdn
)) : '';
123 var slashAdded
= false;
125 // store options for OPTIONS request
126 // only used if OPTIONS request
129 // middleware and routes
130 var stack
= self
.stack
;
132 // for options requests, respond with a default if nothing else responds
133 if (method
=== 'options') {
135 done = function(err
) {
136 if (err
|| options
.length
=== 0) return old(err
);
138 var body
= options
.join(',');
139 return res
.set('Allow', body
).send(body
);
143 (function next(err
) {
144 if (err
=== 'route') {
148 var layer
= stack
[idx
++];
154 req
.url
= req
.url
.substr(1);
158 req
.url
= protohost
+ removed
+ req
.url
.substr(protohost
.length
);
159 req
.originalUrl
= req
.originalUrl
|| req
.url
;
163 var path
= parseUrl(req
).pathname
;
164 if (undefined == path
) path
= '/';
166 if (!layer
.match(path
)) return next(err
);
168 // route object and not middleware
169 var route
= layer
.route
;
171 // if final route, then we support options
173 // we don't run any routes with error first
180 // we can now dispatch to the route
181 if (method
=== 'options' && !route
.methods
['options']) {
182 options
.push
.apply(options
, route
._options());
186 req
.params
= req
.params
|| {};
188 for (var key
in layer
.params
) {
189 req
.params
[key
] = layer
.params
[key
];
192 // this should be done for the layer
193 return self
.process_params(layer
, req
, res
, function(err
) {
199 return layer
.handle(req
, res
, next
);
205 function trim_prefix() {
206 var c
= path
[layer
.path
.length
];
207 if (c
&& '/' != c
&& '.' != c
) return next(err
);
209 // Trim off the part of the url that matches the route
210 // middleware (.use stuff) needs to have the path stripped
211 debug('trim prefix (%s) from url %s', removed
, req
.url
);
212 removed
= layer
.path
;
213 req
.url
= protohost
+ req
.url
.substr(protohost
.length
+ removed
.length
);
215 // Ensure leading slash
216 if (!fqdn
&& '/' != req
.url
[0]) {
217 req
.url
= '/' + req
.url
;
221 debug('%s %s : %s', layer
.handle
.name
|| 'anonymous', layer
.path
, req
.originalUrl
);
222 var arity
= layer
.handle
.length
;
225 layer
.handle(err
, req
, res
, next
);
229 } else if (arity
< 4) {
230 layer
.handle(req
, res
, next
);
242 * Process any parameters for the route.
247 proto
.process_params = function(route
, req
, res
, done
) {
248 var params
= this.params
;
250 // captured parameters from the route, keys and values
251 var keys
= route
.keys
;
254 if (!keys
|| keys
.length
=== 0) {
264 // process params in order
265 // param callbacks can be async
266 function param(err
) {
271 if (i
>= keys
.length
) {
277 paramVal
= key
&& req
.params
[key
.name
];
278 paramCallbacks
= key
&& params
[key
.name
];
281 if (paramCallbacks
&& undefined !== paramVal
) {
282 return paramCallback();
293 // single param callbacks
294 function paramCallback(err
) {
295 var fn
= paramCallbacks
[paramIndex
++];
296 if (err
|| !fn
) return param(err
);
297 fn(req
, res
, paramCallback
, paramVal
, key
.name
);
304 * Use the given middleware function, with optional path, defaulting to "/".
306 * Use (like `.all`) will run for any http METHOD, but it will not add
307 * handlers for those methods so OPTIONS requests will not consider `.use`
308 * functions even if they could respond.
310 * The other difference is that _route_ path is stripped and not visible
311 * to the handler function. The main effect of this feature is that mounted
312 * handlers can operate without any code changes regardless of the "prefix"
315 * @param {String|Function} route
316 * @param {Function} fn
317 * @return {app} for chaining
321 proto
.use = function(route
, fn
){
322 // default route to '/'
323 if ('string' != typeof route
) {
328 if (typeof fn
!== 'function') {
329 var type
= {}.toString
.call(fn
);
330 var msg
= 'Router.use() requires callback functions but got a ' + type
;
331 throw new Error(msg
);
334 // strip trailing slash
335 if ('/' == route
[route
.length
- 1]) {
336 route
= route
.slice(0, -1);
339 var layer
= new Layer(route
, {
340 sensitive
: this.caseSensitive
,
345 // add the middleware
346 debug('use %s %s', route
|| '/', fn
.name
|| 'anonymous');
348 this.stack
.push(layer
);
353 * Create a new Route for the given path.
355 * Each route contains a separate middleware stack and VERB handlers.
357 * See the Route api documentation for details on adding handlers
358 * and middleware to routes.
360 * @param {String} path
365 proto
.route = function(path
){
366 var route
= new Route(path
);
368 var layer
= new Layer(path
, {
369 sensitive
: this.caseSensitive
,
372 }, route
.dispatch
.bind(route
));
376 this.stack
.push(layer
);
380 // create Router#VERB functions
381 methods
.concat('all').forEach(function(method
){
382 proto
[method
] = function(path
, fn
){
383 this.route(path
)[method
](fn
);