【问题标题】:Using express.js I get this error: Uncaught Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client使用 express.js 我收到此错误:未捕获的错误 [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
【发布时间】:2020-08-28 22:08:09
【问题描述】:

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

  • 使用请求正文的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', (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;

提前谢谢你。

【问题讨论】:

  • areParamsRight() 是做什么的?" 请出示该代码。
  • 我已经编辑了帖子。你所要求的在最后。

标签: javascript node.js sqlite express routes


【解决方案1】:

当数据库调用出错时,这段代码会导致发送两个响应:

  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 专家。

【讨论】:

  • 您检查的代码是为了解决问题。在上面你可以看到主要选项(第一个代码块),除了出现错误的情况外,没有 next() 调用。是的,它应该只有一个响应,但代码必须执行 2 次查询,1 次用于更新表,第 2 次用于获取更新的记录。我通过使用serialize() 检查了同步性不是问题。抱歉,如果我没有正确理解您的答案。我很感激。顺便说一句,我已经修复了 sendStatus() 部分。我在开头更新了帖子以指定目标。以前应该这样做的,抱歉。
  • @jSamsa - 好吧,如果您不显示真实代码而不遗漏任何内容,我将无法进一步帮助您。您的问题仅显示一项数据库操作。问题可能在于您的两个操作之间的协调,但您没有共享该代码。
猜你喜欢
  • 2020-09-04
  • 1970-01-01
  • 2022-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多