如何在Express中间件中直接传入req对象内的自定义资源参数?
Express自定义授权中间件:传递动态资源参数的解决方案
问题场景
我在Express中编写自定义授权中间件,核心逻辑是对比req.user.id与目标资源的userId,验证用户是否拥有资源权限。当前实现只能通过传入字符串指定req上存储资源的属性,但希望能直接传入资源(尤其是嵌套较深的数据),比如调用verifyUserAuthorization(['owner'], req.invoices),但直接这么做会因为req在中间件初始化时未定义而报错。
当前的中间件代码:
function verifyUserAuthorization(roles, resource){ return (req, res, next) => { try{ // 验证登录用户是否为资源所有者:对比req.user.id与resource.userId if(roles.includes('owner')){ // 处理资源为数组的情况:检查数组内所有资源的所有权 if(Array.isArray(req[resource])){ let allAuthorized; for(let item of req[resource]){ if(req.user.id !== item.userId){ allAuthorized = false; break; } } if(allAuthorized !== false){ return next(); } // 处理单个资源的情况 }else if(req.user.id === req[resource].userId){ return next(); } } // 未找到有效授权角色时默认抛出401错误 throw new Error("Access denied", {statusCode: 401}); }catch(err){ next(err); } } };
当前调用方式(只能传字符串):
verifyUserAuthorization(['owner'], 'invoices')
解决方案:用函数延迟获取资源
核心问题是中间件初始化阶段(调用verifyUserAuthorization时),请求对象req还未生成,直接传入req.invoices必然报错。解决思路是传入一个资源获取函数,让函数在中间件实际执行时(此时req已存在)再去获取目标资源。
修改后的中间件代码
function verifyUserAuthorization(roles, resourceGetter) { return (req, res, next) => { try { // 兼容字符串和函数两种资源指定方式 let resource; if (typeof resourceGetter === 'string') { // 保留原逻辑:从req上按属性名取资源 resource = req[resourceGetter]; } else if (typeof resourceGetter === 'function') { // 新增逻辑:执行函数获取资源,支持嵌套结构 resource = resourceGetter(req); } else { throw new Error("Invalid resource parameter", { statusCode: 400 }); } if (roles.includes('owner')) { // 批量验证数组资源:所有项必须属于当前用户 if (Array.isArray(resource)) { const allAuthorized = resource.every(item => req.user.id === item.userId); if (allAuthorized) return next(); } // 验证单个资源 else if (resource?.userId === req.user.id) { return next(); } } throw new Error("Access denied", { statusCode: 401 }); } catch (err) { next(err); } }; }
新的调用方式
- 原有字符串调用方式依然可用,兼容旧代码:
verifyUserAuthorization(['owner'], 'invoices')
- 支持传入函数获取嵌套资源,解决深层数据的便捷访问:
// 访问嵌套资源:req.order.invoices verifyUserAuthorization(['owner'], req => req.order.invoices) // 访问单个嵌套资源:req.userProfile verifyUserAuthorization(['owner'], req => req.userProfile)
代码改进点
- 延迟资源获取:通过函数避免初始化阶段访问未定义的
req - 兼容性:保留原字符串调用方式,无需修改现有业务代码
- 代码简化:用
Array.every替代手动循环,更简洁高效 - 容错处理:用
resource?.userId避免资源为undefined时的报错
内容的提问来源于stack exchange,提问作者Brenden
相关产品推荐
相关产品推荐

