exports.Save = function(req, res, next){
var name = req.body.name || "";
if(validator.trim(name).length == 0){
return RequestError(res, '名称为空');
}
if(req.body._id){
// 修改
Category.update({_id: req.body._id}, {$set: {name: name}}, function(err){
if(err) return MongodbError(res, err);
});
}else{
Category.create({name: name}, function(err){
if(err) return MongodbError(res, err);
});
}
return res.json({ success: true, message: '成功' });
好吧,退不出这个黑色的界面了,就这样继续吧~~
Category定义非常简单,只有一个不允许重复的name属性
{
name: {
type: String,
unique: true
}
}
当添加一个重复记录的时候,进程挂掉,报 Can't set headers after they are sent.错误。之前遇到过这个问题,一般是因为某个res.render,res.send,res.json前面没有return,导致重复调用造成的,但仔细看了几遍上面的代码,该return的地方都return掉了,问题依然存在,什么情况?
在网上乱转的时候看到一条回复提到了异步执行几个字,豁然开朗,之前写c#习惯了能少写一行就少写一行,比如说,上面的代码块,一般来讲,代码应该是这样的,
Category.create(obj, function(err){
if(err){
返回错误
}else{
返回成功
}
})
很多人喜欢这样写:
Category.create(obj, function(err){
if(err) return 返回错误
返回成功
})
这样写确实可以省几行代码,也没什么问题,而我却画蛇添足,为了能再省一行,把返回成功的这一句
return res.json({ success: true, message: '成功' });
抽了出来,放到最后面,这要在其他语言里也并没什么问题,因为err的分支都return掉了,即便写数据库出现错误,也应该会被截止运行,不可能走到最后这一句,然而,我错了,下面的分析是我猜的,说的可能不怎么准确,若有问题,欢迎指正:
假设我们的函数是一个流,正常情况下是顺序执行的,但是当遇到文件io,数据库读写这类任务的时候,node会从这个流程中新建一个分支来执行,而主流程还在继续进行,也就是说按我上面的写法,无论如何都会走到最后一句,res.json一个成功的结果,若写mongodb没问题,也就不会触发err,从而触发res来返回结果,自然不会有问题,但如果写数据库出现err,程序就会找到主流程中的回调方法,而回调中又有一个res.json,自然是多次调用res.json导致程序崩溃。
为此,修改上面的代码做个测试
exports.Save = function(req, res, next){
var name = req.body.name || "";
if(validator.trim(name).length == 0){
return RequestError(res, '名称为空');
}
setTimeout(function(){
return res.json({ success: true, message: '成功' });
}, 5000);
return res.json({ success: true, message: '成功' });
}
下面是调用的代码:
$.post("/web/manage/category", {_id: id, name: n.val(), _csrf: csrf}, function (res) {
if (res.success) {
//window.location.reload();
}
console.log(res);
});
运行结果:ajax请求后,返回success:true,几秒钟后,程序崩溃,报Can't set headers after they are sent.错误。
解决的办法也很简单,把成功的结果老老实实写到回调函数中就得了。
exports.Save = function(req, res, next){
var name = req.body.name || "";
if(validator.trim(name).length == 0){
return RequestError(res, '名称为空');
}
if(req.body._id){
// 修改
Category.update({_id: req.body._id}, {$set: {name: name}}, function(err){
if(err) return MongodbError(res, err);
return res.json({ success: true, message: '成功' });
});
}else{
Category.create({name: name}, function(err){
if(err) return MongodbError(res, err);
return res.json({ success: true, message: '成功' });
});
}
问题解决!!
这个问题对专业nodejs程序员来讲应该是很低级的错误,但对像我一样从其他语言转过来的人来说还是个不大不小的坑,有时候思维定势很可怕,js入门太简单,以至于不愿意定下心来好好的深入的进行研究,往往知其然不知其所以然,引以为戒