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.js
和authService.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]: 发送到客户端后无法设置标头的主要内容,如果未能解决你的问题,请参考以下文章