员工休假管理系统非管理员访问路由触发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
相关产品推荐
相关产品推荐

