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

员工休假管理系统非管理员访问路由触发Headers已发送错误排查

问题根源与修复方案

1. 核心问题定位

服务器抛出"cannot set headers after they are sent to client"错误的原因是:当非管理员访问权限受限路由时,权限校验中间件返回错误响应后仍调用了next(),导致后续控制器继续执行并再次发送响应,造成重复设置HTTP响应头。此外,部分控制器未处理资源不存在的情况,且错误捕获后多余调用next()也会引发类似问题。


2. 修复认证中间件

修改AuthorizeRoles和isAuthenticated中间件,确保返回错误响应后终止请求流程,不再触发后续逻辑:

const jwt = require("jsonwebtoken");
const employeeModels = require("../models/employeeModels");

exports.isAuthenticated = async (req, res, next) => {
    try {
        const { token } = req.cookies;
        if (!token)
            return res
                .status(400)
                .json({ success: false, error: "Please Login First" });
        const decodedData = jwt.verify(token, "njuebuipoqdjbcibwjcnowdopq");
        req.employee = await employeeModels.findById(decodedData.id);
        next();
    } catch (e) {
        // 返回错误后终止流程,不调用next()
        return res.status(500).json({
            success: false,
            error: e.message,
        });
    }
};

exports.AuthorizeRoles = (...role) => {
    return (req, res, next) => {
        if (!role.includes(req.employee.role)) {
            // 返回权限错误后终止流程
            return res.status(403).json({
                success: false,
                error: "Only Admin is Allowed to Access this route",
            });
        }
        next();
    };
};

3. 修复休假控制器逻辑

补充资源不存在的判断,移除错误捕获后多余的next()调用,确保每个请求仅返回一次响应:

exports.createLeave = async (req, res) => {
    try {
        const { reason, noOfDays } = req.body;
        const leave = await (
            await LeaveModel.create({
                reason,
                noOfDays,
                employee: req.employee._id,
            })
        ).populate({
            path: "employee",
        });
        res.status(201).json({
            success: true,
            message: "leave Has Been Submitted Successfully",
            leave,
        });
    } catch (e) {
        return res.status(400).json({
            success: false,
            error: e.message,
        });
    }
};

// 获取所有休假(管理员专属)
exports.getAllLeaves = async (req, res) => {
    try {
        const leaves = await LeaveModel.find().populate("employee");
        // 空数组也返回响应,避免请求挂起
        return res.status(200).json({
            success: true,
            leaves,
        });
    } catch (e) {
        return res.status(500).json({
            success: false,
            error: e.message,
        });
    }
};

exports.updateLeaveStatus = async (req, res) => {
    try {
        const leave = await LeaveModel.findByIdAndUpdate(req.params.id, req.body, {
            new: true,
            runValidators: true,
        });
        if (!leave) {
            return res.status(404).json({
                success: false,
                error: "Leave not found",
            });
        }
        return res.status(200).json({
            success: true,
            message: "Leave Status Updated Successfully",
            leave,
        });
    } catch (e) {
        return res.status(500).json({
            success: false,
            error: e.message,
        });
    }
};

exports.deleteLeave = async (req, res) => {
    try {
        const leave = await LeaveModel.findByIdAndDelete(req.params.id);
        if (!leave) {
            return res.status(404).json({
                success: false,
                error: "Leave not found",
            });
        }
        return res.status(200).json({
            success: true,
            message: "Leave Deleted Successfully",
        });
    } catch (e) {
        return res.status(500).json({
            success: false,
            error: e.message,
        });
    }
};

// 获取单个休假详情(管理员专属)
exports.getSingleLeave = async (req, res) => {
    try {
        const leave = await LeaveModel.findById(req.params.id).populate("employee");
        if (!leave) {
            return res.status(404).json({
                success: false,
                error: "Leave not found",
            });
        }
        return res.status(200).json({
            success: true,
            leave,
        });
    } catch (e) {
        return res.status(500).json({
            success: false,
            error: e.message,
        });
    }
};

4. 路由代码无需调整

当前路由的中间件顺序(isAuthenticated → AuthorizeRoles → 控制器)符合权限校验逻辑,保持原代码即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 16:40:27