nodejs 中间件执行在 return 之后继续
nodejs middleware execution continues after return
我有以下中间件:
const mongoose = require('mongoose');
module.exports = function(req, res, next) {
const keys = Object.keys(req.params);
keys.forEach(elem => {
if (
(elem.includes('id') || elem.includes('Id')) &&
!mongoose.Types.ObjectId.isValid(req.params[elem])
)
return res
.status(400)
.json({ msg: `id: ${req.params[elem]} is invalid` });
});
next();
};
它在获取请求中被调用:
// @route GET api/movies/:id
// @desc Get a movie with specified id from db
// @access Public
router.get('/:id', checkId, async (req, res) => {
const movie = await Movie.findById(req.params.id);
res.json(movie);
});
当我在邮递员中使用无效 ID(例如:1234)发出请求时,我收到的正确响应是 400,其中包含 msg: 'id 1234 is invalid' 但执行仍然传递给请求回调代码,错误是当我尝试访问具有无效 ID 的数据库时抛出。
所以问题是为什么中间件仍然允许执行 next(),即使它已经返回 400?
您需要通过调用 next("some-error")
告诉路由器出现问题。例如,您可以这样做:
module.exports = function(req, res, next) {
const keys = Object.keys(req.params);
keys.forEach(elem => {
if (
(elem.includes('id') || elem.includes('Id')) &&
!mongoose.Types.ObjectId.isValid(req.params[elem])
) {
res
.status(400)
.json({ msg: `id: ${req.params[elem]} is invalid` });
return next("invalidinput");
}
});
next();
};
或者如果您愿意,您可以通过在路由器外部设置结果来更通用,如下所示:
在你的中间件中:
module.exports = function(req, res, next) {
const keys = Object.keys(req.params);
keys.forEach(elem => {
if (
(elem.includes('id') || elem.includes('Id')) &&
!mongoose.Types.ObjectId.isValid(req.params[elem])
) {
// === Report the error and let the router handle it
return next({
type: "invalidinput",
msg: `id: ${req.params[elem]} is invalid`
);
}
});
next();
};
然后在路由器的底部:
// handle any errors
router.use(err, req, res, next) => {
if (err) {
if (err.type === "invalidinput") {
return req.status(400).json({msg: err.msg});
}
else {
return res.status(500).json({msg: "Internal error."});
}
}
return next();
}
另一种可能的解决方案是将 forEach 转换为经典的 for 循环,从而使这个中间件 运行 同步
module.exports = function(req, res, next) {
const keys = Object.keys(req.params);
for (let i = 0; i < keys.length; i++) {
if (
(keys[i].includes('id') || keys[i].includes('Id')) &&
!mongoose.Types.ObjectId.isValid(req.params[keys[i]])
)
return res
.status(400)
.json({ msg: `id: ${req.params[keys[i]]} is invalid` });
}
next();
};
我有以下中间件:
const mongoose = require('mongoose');
module.exports = function(req, res, next) {
const keys = Object.keys(req.params);
keys.forEach(elem => {
if (
(elem.includes('id') || elem.includes('Id')) &&
!mongoose.Types.ObjectId.isValid(req.params[elem])
)
return res
.status(400)
.json({ msg: `id: ${req.params[elem]} is invalid` });
});
next();
};
它在获取请求中被调用:
// @route GET api/movies/:id
// @desc Get a movie with specified id from db
// @access Public
router.get('/:id', checkId, async (req, res) => {
const movie = await Movie.findById(req.params.id);
res.json(movie);
});
当我在邮递员中使用无效 ID(例如:1234)发出请求时,我收到的正确响应是 400,其中包含 msg: 'id 1234 is invalid' 但执行仍然传递给请求回调代码,错误是当我尝试访问具有无效 ID 的数据库时抛出。
所以问题是为什么中间件仍然允许执行 next(),即使它已经返回 400?
您需要通过调用 next("some-error")
告诉路由器出现问题。例如,您可以这样做:
module.exports = function(req, res, next) {
const keys = Object.keys(req.params);
keys.forEach(elem => {
if (
(elem.includes('id') || elem.includes('Id')) &&
!mongoose.Types.ObjectId.isValid(req.params[elem])
) {
res
.status(400)
.json({ msg: `id: ${req.params[elem]} is invalid` });
return next("invalidinput");
}
});
next();
};
或者如果您愿意,您可以通过在路由器外部设置结果来更通用,如下所示:
在你的中间件中:
module.exports = function(req, res, next) {
const keys = Object.keys(req.params);
keys.forEach(elem => {
if (
(elem.includes('id') || elem.includes('Id')) &&
!mongoose.Types.ObjectId.isValid(req.params[elem])
) {
// === Report the error and let the router handle it
return next({
type: "invalidinput",
msg: `id: ${req.params[elem]} is invalid`
);
}
});
next();
};
然后在路由器的底部:
// handle any errors
router.use(err, req, res, next) => {
if (err) {
if (err.type === "invalidinput") {
return req.status(400).json({msg: err.msg});
}
else {
return res.status(500).json({msg: "Internal error."});
}
}
return next();
}
另一种可能的解决方案是将 forEach 转换为经典的 for 循环,从而使这个中间件 运行 同步
module.exports = function(req, res, next) {
const keys = Object.keys(req.params);
for (let i = 0; i < keys.length; i++) {
if (
(keys[i].includes('id') || keys[i].includes('Id')) &&
!mongoose.Types.ObjectId.isValid(req.params[keys[i]])
)
return res
.status(400)
.json({ msg: `id: ${req.params[keys[i]]} is invalid` });
}
next();
};