You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express/Passport.js中登录异常与Cannot set headers报错求助

Express/Passport.js 「Cannot set headers after they are sent to the client」错误排查

问题现象

  • 数据库中已有记录的用户无法直接登录;
  • 新用户注册成功后登录,服务器抛出「Cannot set headers after they are sent to the client」错误,已知该错误通常由多次发送HTTP响应导致,但未明确触发原因。

相关代码

app.js 中的Kakao策略配置

passport.use(new KakaoStrategy(
{
    clientID: kakao_key,
    callbackURL: '/userSchemaAPI/login/kakao/callback',
},
async (accessToken, refreshToken, profile, done) => {
    try {
        const foundUser = await User.findOne(
            {
                snsId: profile.id,
                provider: 'kakao',
            },
        );
        if (foundUser) {
            return done(null, foundUser);
        }
        else{
            return done(null, false, profile);
        } 
    } catch (error) {
        return done(error);
    }
},
),

路由代码

router.get('/login/kakao', passport.authenticate('kakao'));

router.get('/login/kakao/callback', (req, res, next) => {
    passport.authenticate('kakao', function (err, user, info){
        if (err) {
            return next(err);
        }
        if (!user) { 
            const { id } = info;
            req.session.joinUser = {
                snsId: id,
                email: info._json.kakao_account.email,
                username: info._json.properties.nickname,
            };
            return req.session.save(() => {
                res.redirect('/userSchemaAPI/register/kakao');
            });
        }
        return req.login(user, function (error){
            if (error) {
                return next(error);
            }
            return res.redirect('/');
        });
    })(req, res, next);
});

注册接口代码(POST /userSchemaAPI/register/kakao)

module.exports.createNewKakaoUser = async(req,res,next)=>{
try {
    const { snsId, username, email } = req.session.joinUser;  
    const user = await User.create({
        provider : 'kakao',
        snsId : snsId,
        email: email,
        username: req.body.username || username,
    });
    req.session.regenerate(() => { 
        req.login(user, (error) => { 
            if (error) {
                return next(error);
            }
            return res.redirect('/');
        });
    });
} catch (error) {
    console.error(error);
    next(error);
}
}

问题分析与解决方案

1. 「Cannot set headers after they are sent to the client」错误原因

该错误源于多次发送HTTP响应:注册接口中req.session.regenerate是异步回调式API,函数执行到该语句后会直接走到try块末尾,若后续有其他中间件尝试发送响应,就会和回调内的res.redirect冲突。

修复方案:改用async/await风格处理异步操作

使用util.promisify将回调式API转为Promise,确保所有异步操作完成后再发送响应:

const util = require('util');

module.exports.createNewKakaoUser = async(req,res,next)=>{
try {
    const { snsId, username, email } = req.session.joinUser;  
    const user = await User.create({
        provider : 'kakao',
        snsId : snsId,
        email: email,
        username: req.body.username || username,
    });

    // 等待session重置完成
    await util.promisify(req.session.regenerate.bind(req.session))();
    // 等待用户登录操作完成
    await util.promisify(req.login.bind(req))(user);

    return res.redirect('/');
} catch (error) {
    console.error(error);
    next(error);
}
}

2. 已有用户无法登录的原因

大概率是缺少Passport的序列化/反序列化配置,Passport需要这两个函数管理用户在Session中的存储与恢复。

修复方案:添加序列化与反序列化函数

在app.js中补充以下代码:

// 将用户ID存入Session
passport.serializeUser((user, done) => {
    done(null, user.id);
});

// 从Session取出ID,查询完整用户信息
passport.deserializeUser(async (id, done) => {
    try {
        const user = await User.findById(id);
        done(null, user);
    } catch (err) {
        done(err);
    }
});

3. 额外语法修复

移除Kakao策略配置末尾的多余逗号,避免语法错误:

passport.use(new KakaoStrategy(
{
    clientID: kakao_key,
    callbackURL: '/userSchemaAPI/login/kakao/callback',
},
async (accessToken, refreshToken, profile, done) => {
    try {
        const foundUser = await User.findOne(
            {
                snsId: profile.id,
                provider: 'kakao',
            },
        );
        if (foundUser) {
            return done(null, foundUser);
        } else {
            return done(null, false, profile);
        } 
    } catch (error) {
        return done(error);
    }
} // 移除此处多余逗号
));

内容的提问来源于stack exchange,提问作者muchmuch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 00:10:51