问题
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.
回答1:
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.
来源:https://stackoverflow.com/questions/35297384/express-middleware-next-and-promises