繁体   English   中英

使用 express.js 我收到此错误:未捕获的错误 [ERR_HTTP_HEADERS_SENT]: 发送到客户端后无法设置标头

[英]Using express.js I get this error: Uncaught Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

我想就这个问题寻求帮助。 这段代码的目标是:

  • 更新artist使用从信息指定的艺术家ID artist请求主体的属性,并将其保存到数据库中。 在响应主体的artist属性上返回带有更新艺术家的 200 响应
  • 如果缺少任何必填字段,则返回 400 响应
  • 如果具有提供的artist ID 的artist不存在,则返回 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', (req, res, next) => {
  if(areParamsRight(req.body.artist)){   
    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) }
      });
      next();
    })
  } else {
    res.setStatus(400);
  }
});

artistsRouter.put('/:artistId', (req, res, next) => {
  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. ')
    }
  })
})

在辅助函数下面。 它似乎工作正常,因为它也用于 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, 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();
        })
    } else {
        res.setStatus(400);
    }
});

可以看到,只要areParamsRight()返回 true,那么代码就会一直执行db.run()然后调用next() 但是,如果db.run()有错误,那么代码也会调用next(error) 但是由于这将被异步调用,对next()的调用已经执行,这将尝试向同一请求发送第二个响应,因此可能会生成您看到的错误类型(尝试向同一个请求发送两个响应)要求)。

在这里想要的行为是什么有点令人困惑。 首先,您只想发送一个响应,而不是两个。 但是,由于这是一个.put()请求处理程序而不是中间件处理程序,因此您似乎应该在这里发送响应而不是调用next() 我认为应该是这样的:

artistsRouter.put('/:artistId', (req, res, next) => {
    if (areParamsRight(req.body.artist)) {
        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);                // report error
                } else {
                    res.sendStatus(200);        // report success
                }
            });
        })
    } else {
        res.sendStatus(400);
    }
});

另请注意,您最后有res.setStatus(400) ,我认为您的意思是res.sendStatus(400)

注意:由于您在此路由处理程序中只运行一个数据库查询,因此您似乎不需要在db.serialize()包装东西。 但是,我不是 SQL 专家。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM