发送后无法设置 headers : 多个中间件执行并发
Can't set headers after they are sent : multiple middleware executions concurency
在我的主要 express.js 配置文件中,我 use
两个自定义错误中间件函数:
const error = require('../middlewares/error');
// catch 404 and forward to error handler
app.use(error.notFound);
// if error is not an instanceOf APIError, convert it.
app.use(error.converter);
我用boom来统一错误信息。这是我的错误中间件:
module.exports = {
/**
* Error responder. Send stacktrace only during development
* @public
*/
responder: (err, req, res, next) => {
res.status(err.output.payload.statusCode);
res.json(err.output.payload);
res.end();
},
/**
* If error is not a Boom error, convert it.
* @public
*/
converter: (err, req, res, next) => {
if (env !== 'development') {
delete err.stack;
}
if (err.isBoom) {
return module.exports.responder(err, req, res);
}
if (err.name === 'MongoError' && err.code === 11000) {
const boomedError = boom.conflict('This email already exists');
boomedError.output.payload.stack = err ? err.stack : undefined;
return module.exports.responder(boomedError, req, res);
}
const boomedError = boom.boomify(err, { statusCode: 422 });
return module.exports.responder(boomedError, req, res);
},
/**
* Catch 404 and forward to error responder
* @public
*/
notFound: (req, res) => {
const err = boom.notFound('Not Found');
return module.exports.responder(err, req, res);
},
};
我的问题是,当我对现有电子邮件执行 "register" 操作时,responder()
被执行了三次。一个是我的 boom.conflict 错误,另一个是 "not found"。 (即使我已经完成 res.end()
.
这是寄存器逻辑:
exports.register = async (req, res, next) => {
try {
validationResult(req).throw();
const user = new User(req.body);
const token = generateTokenResponse(user, user.token());
const userTransformed = user.transform();
user.tokens.push({ kind: 'jwt', token });
user.activationId = uuidv1();
await user.save();
res.status(httpStatus.CREATED);
sendValidationEmail(user.activationId);
return res.json({ user: userTransformed });
} catch (err) {
return next(converter(err, req, res, next));
}
};
user.save()
顺便触发这个:
userSchema.pre('save', async function save(next) {
try {
if (!this.isModified('password')) return next();
const rounds = env === 'test' ? 1 : 10;
const hash = await bcrypt.hash(this.password, rounds);
this.password = hash;
return next();
} catch (err) {
return next(converter(err));
}
});
调用 res.end()
只是告诉响应流您已完成发送数据。在这种情况下您不需要它,因为调用 res.json()
会为您完成。
但是,这与告诉 Express 您已完成请求处理不同。仅仅因为您已发送回复并不一定意味着您没有工作要做。
告诉 Express 您已经完成的方式是不调用 next()
。 Express 假定您默认已完成,并且只有在您调用 next()
.
时才会继续执行 middleware/routing 链
所以这一行:
return next(converter(err, req, res, next));
应该是:
converter(err, req, res, next);
同样,您对 converter()
的另一个调用也不应调用 next()
。
在我的主要 express.js 配置文件中,我 use
两个自定义错误中间件函数:
const error = require('../middlewares/error');
// catch 404 and forward to error handler
app.use(error.notFound);
// if error is not an instanceOf APIError, convert it.
app.use(error.converter);
我用boom来统一错误信息。这是我的错误中间件:
module.exports = {
/**
* Error responder. Send stacktrace only during development
* @public
*/
responder: (err, req, res, next) => {
res.status(err.output.payload.statusCode);
res.json(err.output.payload);
res.end();
},
/**
* If error is not a Boom error, convert it.
* @public
*/
converter: (err, req, res, next) => {
if (env !== 'development') {
delete err.stack;
}
if (err.isBoom) {
return module.exports.responder(err, req, res);
}
if (err.name === 'MongoError' && err.code === 11000) {
const boomedError = boom.conflict('This email already exists');
boomedError.output.payload.stack = err ? err.stack : undefined;
return module.exports.responder(boomedError, req, res);
}
const boomedError = boom.boomify(err, { statusCode: 422 });
return module.exports.responder(boomedError, req, res);
},
/**
* Catch 404 and forward to error responder
* @public
*/
notFound: (req, res) => {
const err = boom.notFound('Not Found');
return module.exports.responder(err, req, res);
},
};
我的问题是,当我对现有电子邮件执行 "register" 操作时,responder()
被执行了三次。一个是我的 boom.conflict 错误,另一个是 "not found"。 (即使我已经完成 res.end()
.
这是寄存器逻辑:
exports.register = async (req, res, next) => {
try {
validationResult(req).throw();
const user = new User(req.body);
const token = generateTokenResponse(user, user.token());
const userTransformed = user.transform();
user.tokens.push({ kind: 'jwt', token });
user.activationId = uuidv1();
await user.save();
res.status(httpStatus.CREATED);
sendValidationEmail(user.activationId);
return res.json({ user: userTransformed });
} catch (err) {
return next(converter(err, req, res, next));
}
};
user.save()
顺便触发这个:
userSchema.pre('save', async function save(next) {
try {
if (!this.isModified('password')) return next();
const rounds = env === 'test' ? 1 : 10;
const hash = await bcrypt.hash(this.password, rounds);
this.password = hash;
return next();
} catch (err) {
return next(converter(err));
}
});
调用 res.end()
只是告诉响应流您已完成发送数据。在这种情况下您不需要它,因为调用 res.json()
会为您完成。
但是,这与告诉 Express 您已完成请求处理不同。仅仅因为您已发送回复并不一定意味着您没有工作要做。
告诉 Express 您已经完成的方式是不调用 next()
。 Express 假定您默认已完成,并且只有在您调用 next()
.
所以这一行:
return next(converter(err, req, res, next));
应该是:
converter(err, req, res, next);
同样,您对 converter()
的另一个调用也不应调用 next()
。