如何解决使用express.js时,出现以下错误:未捕获的错误[ERR_HTTP_HEADERS_SENT]:将标头发送到客户端后无法设置标头
我想在此问题上寻求帮助。 该代码的目标是:
- 使用来自请求正文的
artist
属性的信息,用指定的艺术家ID更新artist
,并将其保存到数据库中。在响应正文的artist
属性上返回带有更新的艺术家的200响应 - 如果缺少任何必填字段,则返回400响应
- 如果提供的
artist
ID的艺术家不存在,则返回404响应。
这是失败的路线:
artistsRouter.put('/:artistId',(req,res,next) => {
if(areParamsRight(req.body.artist)){ // areParamsRight checks for the correct types and body's fields
db.serialize(() => {
db.run('UPDATE Artist SET name = $name,date_of_birth = $date_of_birth,biography = $biography WHERE id = $iD',{ $name: req.body.artist.name,$date_of_birth: req.body.artist.dateOfBirth,$biography: req.body.artist.biography,$is_currently_employed: req.body.artist.isCurrentlyEmployed,$iD: req.artist.id },(error) => { if(error) { next(error) }
});
db.get(`SELECT * FROM Artist WHERE id = $thisId `,{ $thisId: req.artist.id },(err,artist) => {
if(err){
next(err);
} else {
res.status(200).send(artist);
console.log('response sent. ')
}
})
})
} else {
res.setStatus(400);
}
});
我尝试了以下操作:
- 使用.serialize()避免出现竞争状况。
- 验证SQL是否有效。
- 验证了请求中的数据类型。
- 检查是否已附加req.artist.id。
- 划分路线:
artistsRouter.put('/:artistId',next) => {
if(areParamsRight(req.body.artist)){
db.serialize(() => {
db.run('UPDATE Artist SET name = $name,(error) => { if(error) { next(error) }
});
next();
})
} else {
res.setStatus(400);
}
});
artistsRouter.put('/:artistId',next) => {
db.get(`SELECT * FROM Artist WHERE id = $thisId `,artist) => {
if(err){
next(err);
} else {
res.status(200).send(artist);
console.log('response sent. ')
}
})
})
在助手功能下面。看起来不错,因为它也用于POST路由中。
// this function will accept req object and will return true if params are the proper type.
function areParamsRight(obj) {
const name = obj.name,dateOfBirth = obj.dateOfBirth,biography = obj.biography;
if( Object.keys(obj).includes('name' && 'dateOfBirth' && 'biography')
&& typeof name === 'string' && typeof dateOfBirth === 'string' && typeof biography === 'string' ){
return true;
} else return false;
}
module.exports = areParamsRight;
谢谢。
解决方法
当数据库调用发生错误时,这段代码可能导致发送两个响应:
db.run('UPDATE Artist SET name = $name,date_of_birth = $date_of_birth,biography = $biography WHERE id = $iD',{ $name: req.body.artist.name,$date_of_birth: req.body.artist.dateOfBirth,$biography: req.body.artist.biography,$is_currently_employed: req.body.artist.isCurrentlyEmployed,$iD: req.artist.id },(error) => { if(error) { next(error) }
});
next();
如果以这种方式格式化代码,使代码流变得更加清晰:
artistsRouter.put('/:artistId',(req,res,next) => {
if (areParamsRight(req.body.artist)) {
db.serialize(() => {
db.run('UPDATE Artist SET name = $name,{
$name: req.body.artist.name,$iD: req.artist.id
},(error) => {
if (error) {
next(error)
}
});
next();
})
} else {
res.setStatus(400);
}
});
您可以看到,只要areParamsRight()
返回true,那么代码将始终执行db.run()
,然后调用next()
。但是,如果db.run()
中存在错误,则代码也会调用next(error)
。但是,由于该调用将被异步调用,因此对next()
的调用已经执行了,这将尝试向同一请求发送第二个响应,从而可能生成您看到的错误类型(尝试将两个响应发送给相同的请求)。
这里想要的行为有点令人困惑。首先,您只想发送一个响应,而不是两个。但是,由于这是一个.put()
请求处理程序,而不是中间件处理程序,因此您似乎应该在此处发送响应,而不是调用next()
。我认为可能应该是这样:
artistsRouter.put('/:artistId',(error) => {
if (error) {
next(error); // report error
} else {
res.sendStatus(200); // report success
}
});
})
} else {
res.sendStatus(400);
}
});
还请注意,您结尾处有res.setStatus(400)
,我想您的意思是res.sendStatus(400)
。
注意:由于您仅在此路由处理程序中在此处运行一个数据库查询,因此似乎不需要将内容包装在db.serialize()
中。但是,我不是SQL方面的专家。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。