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

Express中isAuthor中间件报错改造:实现跨模型复用

Express通用作者校验中间件改造方案

问题背景

需要实现一个通用中间件,检查当前登录用户是否为内容发布者,仅允许其编辑自己的帖子,同时要让中间件独立于模型实现复用。当前实现的中间件和路由在使用时报错。

当前中间件代码

const asyncHandler = require("express-async-handler");
const { carSchema } = require("./schemas.js");
const Car = require("./models/car");

module.exports.isAuthor = asyncHandler(async (req, res, next, Model) => {
  const { id } = req.params;
  const document = await Model.findById(id);
  if (!document.author.equals(req.user._id)) {
    req.flash("error", "Not allowed");
    return res.redirect(`/${Model.collection.name}/${id}`);
  }
  next();
});

当前路由代码

const express = require("express");
const router = express.Router();
const asyncHandler = require("express-async-handler");
const Car = require("../models/car");
const { isLoggedIn, isAuthor, validateCar } = require("../middleware");

router.get(
  "/:id/edit",
  isLoggedIn,
  isAuthor.bind(null, Car),
  asyncHandler(async (req, res) => {
    const car = await Car.findById(req.params.id);
    if (!car) {
      req.flash("error", "This ad doesnt exist");
      return res.redirect("/cars");
    }
    res.render("cars/editCar", { car });
  })
);

报错信息

Cannot destructure property 'id' of 'req.params' as it is undefined.
TypeError: Cannot destructure property 'id' of 'req.params' as it is undefined. at middleware.js:26:11 at asyncUtilWrap (node_modules\express-async-handler\index.js:3:20) at Layer.handle [as handle_request] (node_modules\express\lib\router\layer.js:95:5) at next (node_modules\express\lib\router\route.js:144:13) at module.exports.isLoggedIn (middleware.js:11:3) at Layer.handle [as handle_request] (node_modules\express\lib\router\layer.js:95:5) at next (node_modules\express\lib\router\route.js:144:13) at Route.dispatch (node_modules\express\lib\router\route.js:114:3) at Layer.handle [as handle_request] (node_modules\express\lib\router\layer.js:95:5) at node_modules\express\lib\router\index.js:284:15

错误原因

核心问题是中间件的参数传递方式错误。Express中间件的固定参数顺序是(req, res, next),但用isAuthor.bind(null, Car)时,会把Car作为第一个参数传入中间件函数,导致原函数里的req被替换成了Car,真正的req变成了第二个参数,自然无法读取req.params。

改造方案

把isAuthor改成高阶函数,让它接收Model作为参数,然后返回一个符合Express规范的中间件函数。这样既可以复用,又能正确获取请求对象。

改造后的中间件代码

const asyncHandler = require("express-async-handler");

module.exports.isAuthor = (Model, options = {}) => {
  // 允许自定义id参数名,默认取req.params.id
  const idField = options.idField || 'id';
  
  return asyncHandler(async (req, res, next) => {
    const docId = req.params[idField];
    if (!docId) {
      req.flash("error", "缺少文档ID");
      return res.redirect("back");
    }

    const document = await Model.findById(docId);
    if (!document) {
      req.flash("error", "该内容不存在");
      return res.redirect(`/${Model.collection.name}`);
    }

    // 校验用户是否为作者
    if (!document.author.equals(req.user._id)) {
      req.flash("error", "无权限编辑该内容");
      return res.redirect(`/${Model.collection.name}/${docId}`);
    }

    // 可选:把文档挂载到req上,后续路由无需再查询
    req.doc = document;
    next();
  });
};

改造后的路由使用方式

不需要再用bind,直接调用isAuthor(Car)返回中间件即可:

const express = require("express");
const router = express.Router();
const asyncHandler = require("express-async-handler");
const Car = require("../models/car");
const { isLoggedIn, isAuthor, validateCar } = require("../middleware");

router.get(
  "/:id/edit",
  isLoggedIn,
  isAuthor(Car), // 直接传入模型
  asyncHandler(async (req, res) => {
    // 可以直接用req.doc,省去重复查询
    const car = req.doc;
    res.render("cars/editCar", { car });
  })
);

额外优化点

  • 支持自定义ID字段:如果路由里用的不是:id而是:postId,可以通过isAuthor(Post, { idField: 'postId' })来适配。
  • 处理文档不存在的情况:原代码没判断document是否为null,改造后增加了该逻辑,避免后续报错。
  • 挂载文档到req对象:减少重复数据库查询,提升性能。
  • 错误提示更清晰:不同场景返回不同的错误信息,方便用户排查。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 08:25:19