问题描述:

There is very simple Express router with handler:

router.get('/users/:userId/roles/:roleId', function(req, res, next){

const roleId = req.params.roleId;

res.rest.resource = UserModel.findOne({ _id: req.params.userId}).exec().then(function(usr) {

console.log(req.params.roleId); // => undefined

console.log(roleId); // => okay here

const result = usr.roles.find( role => String(role._id) === String(roleId));

return result;

});

next();

});

As it seen accessing req.params.roleId within promise returnes undefined. This is true only for cases when next() called outside promise's then.

I'm okay with asyncing and promises and understand that next() will be called before handler in then. But what happening with req.params.roleId? Why and where it mutates? Does middleware called by next() gets same but mutated req?

Note: res.rest.resource used by middleware called later to build right REST-like response.

网友答案:

The code as it is is kind of indeterministic in its execution.

Something mutates the role ID in the next() handler, and since it takes a while for findOne() to eventually dispatch to the then handler, that mutation has already happened.

Without knowing further details of your app, it looks like this might be the correct implementation.

router.get('/users/:userId/roles/:roleId', function(req, res, next) {
    const roleId = req.params.roleId;
    UserModel.findOne({ _id: req.params.userId}).exec().then((usr) => {
        const result = usr.roles.find(role => String(role._id) === String(roleId));
        res.rest.resource = result;
        next(); // <-- only dispatch to next after we find the resource result
    });
});

Edit:

I dug a little deeper. See this little example app:

var express = require('express');
var app = express();

app.use(function (req, res, next) {
    var v = 0 | +new Date();
    console.log("middleware 1 setting foos to ", v);
    req.params.foo = v;
    req.foo = v;
    next();
});

app.use(function (req, res, next) {
    console.log("middleware 2 reading foos and starting timer:", req.params.foo, req.foo);
    setTimeout(function() {
        console.log("middleware 2: foos are now", req.params.foo, req.foo);
    }, 1000);
    next();
});

app.get("/", function(req, res) {
    res.send("params = " + JSON.stringify(req.params) + " and foo = " + req.foo);
});

app.listen(3000);

The output for a request is

middleware 1 setting foos to  -902674369
middleware 2 reading foos and starting timer: undefined -902674369
middleware 2: foos are now undefined -902674369
middleware 1 setting foos to  -902673113
middleware 2 reading foos and starting timer: undefined -902673113
middleware 2: foos are now undefined -902673113

and the browser output is params = {} and foo = -902673113, so it turns out that you are not allowed to touch req.params, but you can add any other properties to the req object and they will travel along fine.

This seems to be because of the route matching layer rewriting params on each step.

相关阅读:
Top