So I am debugging how to test an express router object. Essentially to have 100% coverage. Anyways, what I noticed while debugging is that the router object creates a stack of routes. Cool, except when I look at the stack, params are undefined while keys is an array of my params. Pretty misleading. Then in my service, I can request these so called keys as req.params.
Just wonder why the params do not get populated here. Is this params property only for inherited params? like from router.param
Well, the names of those properties were from before I managed Express.js so I don't know why, only that they are and changing them is not simple with a lot of code floating around using those properties.
Do you mean router.params? This contains only all callbacks added through router.param(). In lib/router/index.js
proto.param = function param(name, fn) {
//...
(this.params[name] = this.params[name] || []).push(fn);
return this;
}
Or do you mean layer.params of each layer stored on router.stack? In this case, in function Layer() of lib/router/layer.js
this.regexp = pathRegexp(path, this.keys = [], opts); // regexp group fills this.keys with parameter names
layer.params is only populated as layer.params[paramName] === paramValue when layer.match() is called in router.handle() (aka router()), and that a match exist to the corresponding path. I am not quite sure what could have been the problem, but I would suggest checking the source code of Layer.prototype.match() in lib/router/layer.js and possibly proto.handle() in lib/router/index.js, to get a better understanding of them.
@kevinkassimo so the in the stack the params will get populated once the route is being navigated to? While the keys are simply there for reference? Also thanks for the direction of where to read.
@JemiloII In Layer.prototype.match() of router/layer.js,
Layer.prototype.match = function match(path) {
var match
if (path != null) {
//...
match = this.regexp.exec(path) // try match the path
}
if (!match) { // current match failed, layer.params is undefined and not populated
this.params = undefined;
this.path = undefined;
return false;
}
// If matched
var keys = this.keys;
var params = this.params;
for (var i = 1; i < match.length; i++) {
var key = keys[i - 1];
var prop = key.name;
var val = decode_param(match[i]) // using the grouping of regexp
if (val !== undefined || !(hasOwnProperty.call(params, prop))) {
params[prop] = val; // populate layer.params HERE!
}
}
return true;
}
Also after reviewing the logic of router.handle(), I believe the steps could indeed be understood as:
router.handle() is invoked with a new given path that requires inspection layer.params is populated using the param names stored in layer.keys as keys to establish key value mappings router.handle then merges current layer.params into req.paramsrouter.handle first try processing all param handlers added through router.param()router.handle then calls your provided path handlers, by internally calling layer.handle_request(req, res, next), and that all param data could be accessed through req.params next() in your provided handler, the router.handle() continues tracing down the router.stack. For each extra match, goto 2. Else done.So basically I believe you are correct.
So following along here, I think the original question is answered. If not, please let us know and the issue can be reopened.
Most helpful comment
@JemiloII In
Layer.prototype.match()ofrouter/layer.js,Also after reviewing the logic of
router.handle(), I believe the steps could indeed be understood as:router.handle()is invoked with a new given path that requires inspection2.1 before this path match returned true,
layer.paramsis populated using the param names stored inlayer.keysas keys to establish key value mappingsrouter.handlethen merges currentlayer.paramsintoreq.params3.1
router.handlefirst try processing all param handlers added throughrouter.param()3.2
router.handlethen calls your provided path handlers, by internally callinglayer.handle_request(req, res, next), and that all param data could be accessed throughreq.paramsnext()in your provided handler, therouter.handle()continues tracing down therouter.stack. For each extra match, goto 2. Else done.So basically I believe you are correct.