My Express.js app has been throwing Cannot read property 'req' of undefined
. In essence, it listens for a GET
request, grab the content
query, and then reply with a table. Here's the parts that present the problem.
index.js
var panels = require('./modules/panels.js');
app.get('/panel', function (req, res) {
var user;
if (user = req.session.user) {
panels.getContents(req.query.content, user.innId, res.send);
} else {
res.sendStatus(401);
}
});
modules/panels.js
exports.getContents = function(panelName, innId, callback) {
var response = "";
switch (panelName) {
case 'tenants':
con.query(queryString, queryParams, function(err, rows) {
if (err) {
handle(err);
} else {
if (rows.length == 0) {
var tenants = 0;
var debtors = 0;
} else {
var tenants = rows[0].tenants;
var debtors = rows[0].length;
}
response = convertToHTMLTable(rows);
}
callback(response); /*THE ERROR POINTS HERE */
});
break;
/* other cases here */
default:
response += "Invalid Request";
callback(response);
}
}
What did I do wrong? My guess is that I'm not suppose to pass res.send
as a callback. So, how can I fix it?
I also experienced this error, and after chewing on the error message for a bit and staring at my code for too long, It clicked.
I (and the asker above) had code that looked like this:
somethingAsync
.then(res.send) // <-- storing send() divorced from parent object "res"
}
The problem is that when res.send gets called later, it is divorced from its dynamic scope--which means that calls to this
inside of the send
method, which would normally refer to res
, now refer to global
. Evidently, res.send
contains a reference to this.req
, given the error message.
An equivalent scenario:
res = {
send: function() {
console.log(this.originalMessage.req);
},
originalMessage: { req: "hello SO" },
};
res.send();
// # "hello SO"
const x = {};
x.send = res.send;
x.send();
// # Uncaught TypeError: Cannot read property 'req' of undefined
Or, to put it another way:
new Promise(_ => _())
.then(_ => console.log(this.msg.req));
// # Uncaught (in promise) TypeError: Cannot read property 'req' of undefined
Two solutions:
handler(req, res) {
somethingAsync
.then(() => res.send())
// ^ send will be called as a method of res, preserving dynamic scope
// that is, we're storing a function that, when called, calls
// "res.send()", instead of storing the "send" method of "res" by itself.
}
And a theoretically more idiomatic approach is
handler(req, res) {
somethingAsync
.then(res.send.bind(res))
//^ res is explicitly bound to the saved function as its dynamic scope
}
Pernicious little gotcha of dynamic scoping, that one. Seems like express should ditch the dynamic scoping there, or at least throw a better error...
try this in index.js
panels.getContents(req.query.content, user.innId, res);
and into panels.js
exports.getContents = function(panelName, innId, response) {
var response = "";
switch (panelName) {
case 'tenants':
con.query(queryString, queryParams, function(err, rows) {
if (err) {
handle(err);
} else {
if (rows.length == 0) {
var tenants = 0;
var debtors = 0;
} else {
var tenants = rows[0].tenants;
var debtors = rows[0].length;
}
response.send(convertToHTMLTable(rows));
}
});
break;
/* other cases here */
default:
error += "Invalid Request";
response.send(error)
}
}
When you passed res.send
as an argument to the getContents
function ( panels.getContents(req.query.content, user.innId, res.send);
) it is passed in a new variable and you called it callback
.
So res.send
is now stripped from it's original/supposed-to-be lexical context
/ binding
which is the res
object in this case, and it appears that res.send
uses the req
property in the res
object using this.req
.
What you've done here is unintentionally changing the value of this
for the function send
to be the global object.
there are two ways to fix this problem
panels.getContents(req.query.content, user.innId, () => res.send);
// OR
panels.getContents(req.query.content, user.innId, res.send.bind(res));
in the second solution, you bind the function res.send
to always treat any value of this
to be the res
object ( bind it to the res object
)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.