Express: [ERR_HTTP_HEADERS_SENT]: 发送到客户端后无法设置标头

Posted

技术标签:

【中文标题】Express: [ERR_HTTP_HEADERS_SENT]: 发送到客户端后无法设置标头【英文标题】:Express: [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client 【发布时间】:2019-12-11 08:24:00 【问题描述】:

我已经阅读了几篇关于同一问题的帖子,但我无法弄清楚我最初将标头发送到哪里。这是堆栈跟踪:

我得到一个 204 也似乎很奇怪,因为它添加到数据库中,然后它吐出一个 404。显然有问题,但我根本没有看到它。

我尝试在每个 res.json() 语句中添加返回值。

OPTIONS /api/users/favorites 204 1.822 ms - 0
PATCH /api/users/favorites 404 19.769 ms - 160

Unhandled rejection Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:455:11)
    at ServerResponse.header (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/response.js:771:10)
    at ServerResponse.send (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/response.js:170:12)
    at ServerResponse.json (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/response.js:267:15)
    at /Users/beers/projects/node/cryptopal-api/src/users/usersRouter.js:30:38
From previous event:
    at Builder.Target.then (/Users/beers/projects/node/cryptopal-api/node_modules/knex/lib/interface.js:27:24)
    at /Users/beers/projects/node/cryptopal-api/src/users/usersRouter.js:19:8
    at Layer.handle [as handle_request] (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/route.js:137:13)
    at requireAuth (/Users/beers/projects/node/cryptopal-api/src/middleware/jwt-auth.js:31:5)
    at Layer.handle [as handle_request] (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/layer.js:95:5)
    at /Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/index.js:281:22
    at Function.process_params (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/index.js:335:12)
    at next (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/index.js:275:10)
    at Function.handle (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/index.js:174:3)
    at router (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/index.js:47:12)
    at Layer.handle [as handle_request] (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/index.js:317:13)
    at /Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/index.js:335:12)
    at next (/Users/beers/projects/node/cryptopal-api/node_modules/express/lib/router/index.js:275:10)
    at /Users/beers/projects/node/cryptopal-api/node_modules/body-parser/lib/read.js:130:5
    at invokeCallback (/Users/beers/projects/node/cryptopal-api/node_modules/raw-body/index.js:224:16)
    at done (/Users/beers/projects/node/cryptopal-api/node_modules/raw-body/index.js:213:7)
    at IncomingMessage.onEnd (/Users/beers/projects/node/cryptopal-api/node_modules/raw-body/index.js:273:7)
    at IncomingMessage.emit (events.js:205:15)
    at endReadableNT (_stream_readable.js:1154:12)

这是我的usersRouter.js

require('dotenv').config();
const express = require('express');
// const rp = require('request-promise');

const usersRouter = express.Router()
const jsonParser = express.json()

const UsersService = require('./usersService.js')
const  requireAuth  = require('../middleware/jwt-auth.js')

usersRouter
  .patch('/favorites', requireAuth, (req,res,next) => 
    const db = req.app.get('db');
    const  coinID  = req.body;
    const  user_id  = req;
    console.log(res.headersSent) // EQUALS FALSE

    // get current favorites for user to see if it already exists in db
    UsersService.getUserFavorites(db, user_id)
      .then( response => 
        console.log(res.headersSent) // EQUALS TRUE
        let favExists = false;
        response.favorites.forEach( fav => 
          if(fav == coinID)
            favExists = true;
        )
        if(favExists)
          return res.status(401).json( error: "Coin already exists in favorites" )
        else
          UsersService.addToUserFavorites(db, user_id, coinID)
            .then( response => 
              return res.status(204).json( response )
            )
        
      )
      next()
  );


  module.exports = usersRouter;

如您所见,patch 路由调用中间件函数requireAuth 对用户进行身份验证,然后才会添加收藏夹。

这是那个文件jwt-auth.js

const AuthService = require('../auth/authService.js')

function requireAuth(req, res, next) 
  const authToken = req.get('Authorization') || ''

  let bearerToken
  if (!authToken.toLowerCase().startsWith('bearer ')) 
    return res.status(401).json( error: 'Missing bearer token' )
   else 
    bearerToken = authToken.slice(7, authToken.length)
  

  try 
    const payload = AuthService.verifyJwt(bearerToken);

    AuthService.getUserByEmail(
      req.app.get('db'),
      payload.sub,
    )
      .then(user => 
        if (!user)
          return res.status(401).json( error: 'Unauthorized request' )
        
        next();
      )
      .catch(err => 
        console.error(err)
        next(err)
      )
    req.user_id = payload.user_id;
    next()
   catch(error) 
    return res.status(401).json( error: 'Unauthorized request' )
  


module.exports = 
  requireAuth,

我还将包含usersService.jsauthService.js 文件,因为其中调用了几个函数,但我不认为这就是错误所在。

usersService.js:

const xss = require('xss');
const config = require('../config.js');

const UsersService = 
  getUserByID(db,id)
    return db('cryptopal_users')
      .where( id )
      .first()
  ,
  getUserFavorites(db,id)
    return db('cryptopal_users')
      .where('id', id)
      .first()
  ,
  addToUserFavorites(db,id,favorites)
    return db('cryptopal_users')
      .where('id', id)
      .update(
        favorites: db.raw('array_append(favorites, ?)', [favorites])
      )
  ,


module.exports = UsersService;

authService.js

const xss = require('xss');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const config = require('../config.js');

const AuthService = 
  validatePassword(password)
    if(password.length < 6)
      return "Password must be at least 6 characters"
    
  ,
  hashPassword(password)
    return bcrypt.hash(password,12);
  ,
  comparePasswords(password,hash)
    return bcrypt.compare(password,hash);
  ,
  createJwt(subject, payload) 
    return jwt.sign(payload, config.JWT_SECRET, 
      subject,
      expiresIn: config.JWT_EXPIRY,
      algorithm: 'HS256',
    )
  ,
  checkEmailUnique(db,email)
    return db('cryptopal_users')
      .where( email )
      .first()
      .then(user => !!user)
  ,
  insertUser(db,user)
    return db
      .insert(user)
      .into('cryptopal_users')
      .returning('*')
      .then( ([user]) => user )
  ,
  serializeUser(user)
    return 
      id: user.id,
      name: xss(user.name),
      email: xss(user.email),
      date_created: new Date(user.date_created),
    
  ,
  getUserByEmail(db,email)
    return db('cryptopal_users')
      .where( email )
      .first()
  ,
  verifyJwt(token) 
    return jwt.verify(token, config.JWT_SECRET, 
      algorithms: ['HS256'],
    )
  ,


module.exports = AuthService;

我相信问题出在 jwt-auth.js 文件中的某个地方,但不是 100% 确定。该代码确实一直到最后,它在验证用户身份后将收藏夹插入数据库,但随后引发有关标题的错误。

【问题讨论】:

您是否尝试过检查您的 api 发送的标头?当用户通过身份验证与未通过身份验证时,您收到相同的消息吗? This post 有大量有用的信息可以帮助您进行调试。 @Peter,我发现直到通过在 res.headersSent 中记录 res.headersSent 调用 UsersService.getUserFavorites 之后才发送标头,但 getUserFavorites 中的任何内容都不应该发送标头。跨度> 【参考方案1】:

问题是在patch 路由的最后,我有一个next()。一旦我删除它,它就可以正常工作了。

【讨论】:

以上是关于Express: [ERR_HTTP_HEADERS_SENT]: 发送到客户端后无法设置标头的主要内容,如果未能解决你的问题,请参考以下文章

Express简单使用

Express中文文档

怎么使用express开发web

Express框架请求与响应Express页面

Express框架概述

Express—Express路由请求