【问题标题】:Express: [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the clientExpress: [ERR_HTTP_HEADERS_SENT]: 发送到客户端后无法设置标头
【发布时间】: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 中的任何内容都不应该发送标头。跨度>

标签: javascript express jwt express-jwt


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-06
    • 2020-05-20
    • 2019-06-15
    • 2019-12-25
    • 2022-11-30
    • 1970-01-01
    • 1970-01-01
    • 2021-12-18
    相关资源
    最近更新 更多