lihuayuli 2025-05-26 15:16 采纳率: 100%
浏览 12
已结题

为什么我的后端js代码没法正常运行?

报错:

img

代码:controllers层:

const router = require("../routes/guestBookRoutes");
const guestbookDAO = require("../models/guestbookModel");
const dbDAO = new guestbookDAO("guestbook.db");
//调用model层的constructor方法(构造函数)
// 创建了一个实例对象dbDAO,用于操作嵌入式数据库。
// const dbDAO = new guestbookDAO();
// 创建了一个实例对象dbDAO,用于操作嵌入式数据库。
exports.landing_page = function (req, res) {
  res.send("Hello! Welcome to my application!");
  // dbDAO.init();
  //初始化数据库,即在内存中创建一个空的数组。
};
exports.entries_list = function (req, res) {
  // res.send("<h1>Guestbook</h1>");
  dbDAO
    .getAllEntries()
    .then((list) => {
      //then拿到promise对象,list是resolve的值,也就是数据库中的所有记录。
      res.render("entries", {
        //渲染名为entries的视图
        title: "Guestbook",
        //要渲染的内容,key:value形式,
        //这里的key是mustache文件的title,value是每个循环中的内容,
        // 也就是想显示的内容。
        entries: list,
        //这里的entries要和mustache文件中的{{#entries}}对应起来。
        //这里的list是数据库中的所有记录,要和mustache文件中的{{entries}}对应起来。
      });
    })
    .catch((err) => {
      console.log("err:", err);
    }); //catch在then后面,捕获then中的错误。
};
exports.peter_entries = function (req, res) {
  res.send("<h1>Processing Peter's Entries, see terminal</h1>");
  dbDAO
    .getPetersEntries()
    .then((list1) => {
      //then拿到promise对象,list是resolve的值,也就是数据库中的所有记录。
      res.render("entries_p", {
        //渲染名为entries的视图
        title: "Peter's Guest Book",
        //要渲染的内容,key:value形式,
        //这里的key是mustache文件的title,value是每个循环中的内容,
        // 也就是想显示的内容。
        entries_p: list1,
        //这里的entries要和mustache文件中的{{#entries}}对应起来。
        //这里的list是数据库中的所有记录,要和mustache文件中的{{entries}}对应起来。
      });
    })
    .catch((err) => {
      console.log("err:", err);
    });
};
exports.show_new_entry = function (req, res) {
  res.render("newEntry", {
    title: "New Entry",
  });
};
exports.post_new_entry = function (req, res) {
  if (!req.body.author || !req.body.subject || !req.body.contents) {
    res.status(400).send("Author不能为空!");
    return;
  }
  dbDAO.addEntry(req.body.author, req.body.subject, req.body.contents);
  //跟name属性对应
  res.redirect("/guestbook");
};
exports.show_user_entries = function (req, res) {
  let user = req.params.author; // URL:后的内容,是个形参
  dbDAO
    .getUserEntries(user)
    .then((list) => {
      //then拿到promise对象,list是resolve的值,也就是数据库中的所有记录。
      res.render("entries", {
        //渲染名为entries的视图
        title: user + " Guestbook",
        //要渲染的内容,key:value形式,
        //这里的key是mustache文件的title,value是每个循环中的内容,
        // 也就是想显示的内容。
        entries: list,
        //这里的entries要和mustache文件中的{{#entries}}对应起来。
        //这里的list是数据库中的所有记录,要和mustache文件中的{{entries}}对应起来。
      });
    })
    .catch((err) => {
      console.log("err:", err);
    }); //catch在then后面,捕获then中的错误。
};
exports.update_entry = function (req, res) {
  const _id = req.params.id;
  dbDAO
    .updateEntry(_id)
    .then((list) => {
      //then拿到promise对象,list是resolve的值,也就是数据库中的所有记录。
      res.render("update", {
        //渲染名为entries的视图
        title: user + " Update Guestbook",
        //要渲染的内容,key:value形式,
        //这里的key是mustache文件的title,value是每个循环中的内容,
        // 也就是想显示的内容。
        entries: list,
        //这里的entries要和mustache文件中的{{#entries}}对应起来。
        //这里的list是数据库中的所有记录,要和mustache文件中的{{entries}}对应起来。
      });
    })
    .catch((err) => {
      console.log("err:", err);
    }); //catch在then后面,捕获then中的错误。
  // res.redirect("/guestbook");
};
exports.show_update_entry = function (req, res) {
  const _id = req.params.id; // 从URL获取参数
  // 查询数据库获取要更新的条目(需确保`getEntryById`方法存在)
  dbDAO
    .getEntryById(_id) // 假设模型层有此方法
    .then((entry) => {
      if (!entry) {
        return res.status(404).send("Entry not found");
      }
      res.render("update", {
        title: "Update Entry",
        id: _id, // 将ID传递给视图
        entry: entry, // 将条目数据传递给视图
      });
    })
    .catch((err) => {
      console.error("Error fetching entry for update:", err);
      res.status(500).send("Internal Server Error");
    });
};
exports.delete_entry = function (req, res) {
  const _id = req.params.id;
  dbDAO
    .deleteEntry(_id)
    .then((list) => {
      //then拿到promise对象,list是resolve的值,也就是数据库中的所有记录。
      res.redirect("/guestbook");
    })
    .catch((err) => {
      console.log("err:", err);
    }); //catch在then后面,捕获then中的错误。
};
// exports.new_entry = function (req, res) {
//   res.send("<h1>Not yet implemented:show a new entry page.</h1>");
//   dbDAO.init();
// };
exports.show_user_entries;
exports.about_page = function (req, res) {
  // 返回位于public文件夹下的about.html文件
  res.redirect("about.html");
  //直接重定向,app.use(express.static(public));的作用
  dbDAO.init();
};
exports.image = function (req, res) {
  // 返回位于public/img/logo.jpg的图片文件
  res.redirect("logo.jpg");
  dbDAO.init();
};
exports.status_404 = function (req, res) {
  res.status(404); // 设置HTTP状态码为404
  res.send("Oops! Page not found."); // 返回错误提示
};
exports.status_500 = function (err, req, res, next) {
  console.log("error:", err);
  res.status(500);
  res.type("text/plain");
  res.send("Internal Server Error");
};


router层:

const express = require("express");
const { entries_list } = require("../controllers/guestbookControllers");
const router = express.Router();
const controllers = require("../controllers/guestbookControllers");
router.get("/", controllers.landing_page);
router.get("/guestbook", controllers.entries_list);
router.get("/peter", controllers.peter_entries);
router.get("/new", controllers.show_new_entry);
router.get("/posts/:author", controllers.show_user_entries);
router.get("/update/:id", controllers.show_update_page);
router.post("/update/:id", controllers.update_entry);
router.post("/delete/:id", controllers.delete_entry);
//加上:变成一个变量,可以匹配任何东西
// router.get("/posts", controller.get("/posts", function (request, response) {
//     console.log("filtering for author ", request.query.author);
//   }));
router.post("/new", controllers.post_new_entry);
router.get("/about", controllers.about_page);
router.get("/img/logo.jpg", controllers.image);
router.use(controllers.status_404);
router.use(controllers.status_500);
module.exports = router; //导出路由,非常重要


model层:

const nedb = require("@seald-io/nedb"); //nedb是一个变量
class GuestBook {
  constructor(dbFilePath) {
    if (dbFilePath) {
      this.db = new nedb({ filename: dbFilePath, autoload: true });
      //db是该对象的一个属性,用来存储该数据库的引用信息。
      console.log("DB created:" + dbFilePath);
    } else {
      this.db = new nedb();
    }
  }

  init() {
    this.db.insert(
      {
        subject: "I liked the exhibition",
        contents: "nice",
        published: "2020-02-16",
        author: "Peter",
      },
      function (err, newDoc) {
        if (err) {
          console.log("Error:" + err);
        } else {
          console.log("Peter inserted!");
        }
      }
    );
    this.db.insert(
      {
        subject: "Didn't like it",
        contents: "A really terrible style!",
        published: "2020-02-18",
        author: "Ann",
      },
      function (err, newDoc) {
        if (err) {
          console.log("Error:" + err);
        } else {
          console.log("Ann inserted!");
        }
      }
    );
  }
  addEntry(author_value, subject_value, contents_value) {
    this.db.insert(
      {
        subject: subject_value,
        contents: contents_value,
        published: new Date().toISOString().split("T")[0],
        author: author_value,
      },
      function (err, newDoc) {
        if (err) {
          console.log("Error:" + err);
        } else {
          console.log("日期为:" + new Date().toISOString());
          console.log("addEntry inserted!");
        }
      }
    );
  }

  getAllEntries() {
    //return a Promise object, which can be resolved or rejected
    return new Promise((resolve, reject) => {
      //Promise对象可以记录异步操作是成功还是失败
      //resolve记录成功时的状态,reject记录失败时的报错信息,
      // 同时变化了Promise的状态。
      //避免回调地狱,用Promise代替回调函数。
      this.db.find({}, function (err, entries) {
        if (err) {
          reject(err);
          console.log("getAllEntries Promise rejected:" + err);
        } else {
          resolve(entries);
          console.log("getAllEntries Promise resolved: ", entries);
        }
      });
    });
  }
  getPetersEntries() {
    return new Promise((resolve, reject) => {
      this.db.find({ author: "Peter" }, function (err, entries_p) {
        if (err) {
          reject(err);
          console.log("getPetersEntries Promise rejected:" + err);
        } else {
          resolve(entries_p);
          console.log("getPetersEntries Promise resolved:", entries_p);
        }
      });
    });
  }
  getUserEntries(userName) {
    return new Promise((resolve, reject) => {
      this.db.find({ author: userName }, function (err, entries_u) {
        if (err) {
          reject(err);
          console.log("getUserEntries Promise rejected:" + err);
        } else {
          resolve(entries_u);
          console.log("getUserEntries Promise resolved:", entries_u);
        }
      });
    });
  }

  updateEntry(userID, author, contents) {
    return new Promise((resolve, reject) => {
      this.db.update(
        { _id: userID },
        {
          $set: {
            author: req.body.author, // 从表单获取数据
            contents: req.body.contents,
            published: new Date().toISOString().split("T")[0],
            // 注意这里的写法,$set是用来更新数据的,而不是直接赋值。
            // 这样可以避免覆盖掉其他字段的数据。
          },
        },
        function (err, updatedDoc) {
          if (err) {
            reject(err);
            console.log("updateEntry Promise rejected:" + err);
          } else {
            resolve(updatedDoc);
            console.log("updateEntry Promise resolved:", updatedDoc);
          }
        }
      );
    });
  }
  deleteEntry(userID) {
    return new Promise((resolve, reject) => {
      this.db.delete({ _id: userID }, function (err, db_deleted) {
        if (err) {
          reject(err);
          console.log("deleteEntry Promise rejected:" + err);
        } else {
          resolve(db_deleted);
          console.log("deleteEntry Promise resolved:", +userID + " deleted");
        }
      });
    });
  }
}

module.exports = GuestBook;


view层:
entries.mustache:

<html lang="en">
<head>
    {{>header}}
</head>

<body class="container">
//read-only不可编辑
//点修改按钮,id作为参数,找到对应记录,跳转到对应mustache
//作者只能修改自己的,对应登录的名字
//删除要弹出警告框,避免误操作
    <h1>{{title}}</h1>
    <div class="text-right">
        <a href="/new" class="btn btn-primary">
            Write in the guest book
        </a>
    </div>
    {{#entries}}
    
    <div class="card">
        <div class="card-header">
            {{subject}}
        </div>
        <div class="card-body">
            <div class="card-text">
                {{contents}}
            </div>
            <div class="card-text">
                Written by <a href="/posts/{{author}}">{{author}}</a>,on {{published}}
                <a href="/update/{{id}}" >修改</a>
                <!-- 修改删除链接为表单 -->
<form action="/delete/{{id}}" method="post">
  <button type="submit" class="delete-btn">删除</button>
</form>
            </div>
        </div>
    </div>
    {{/entries}}
    {{^entries}}
            No entries yet!
        {{/entries}}
</body>

</html>
{! <div class="card-text">
{! Written by <a href="/posts/?author={{author}}">{{author}}</a>, on 
{! {{published}}
{! </div>


update.mustache:

<!DOCTYPE html>
<html lang="en">
  <head>
    {{>header}}
  </head>
  <body class="container">
  <h1>{{title}}</h1>
    <form action="/update/{{id}}" method="post" role="form">
      <div class="form-group">
        <label for="author">Author</label>
        <input
          type="text"
          class="form-control"
          id="author"
          name="author"//每一个框一定要有一个name属性
          value="{{entry.author}}"
          required
        />
      </div>
      <div class="form-group">
        <label for="subject">Subject</label>
        <input
          type="text"
          class="form-control"
          id="subject"
          name="subject"
          value="{{subject}}"
          required
        />
        
      </div>
      <div class="form-group">
        <label for="contents">Entry text</label>
        <textarea
          class="form-control"
          id="contents"
          name="contents"
          value="{{contents}}"
          rows="3"
          required
        >
        {{entry.contents}}
        </textarea>
      </div>
      <div class="form-group">
        <input type="submit" value="Post entry" class="btn btn-primary" />
      </div>
    </form>
  </body>
</html>



  • 写回答

5条回答 默认 最新

  • 一杯年华@编程空间 2025-05-26 15:21
    关注

    一杯年华@编程空间帮您解答,本答案结合 ChatGPT4.0和DeepSeek整理

    我曾经遇到过类似的问题,从你提供的报错信息和代码来看,核心问题是路由定义时缺少回调函数。报错信息明确指出 “Route.get() requires a callback function but got a [object Undefined]”,这说明在配置路由时,某个路由的处理函数未正确传递,导致 Express 接收到 undefined 值而非函数。

    问题分析

    从代码中发现,router 层的 guestBookRoutes.js 存在明显错误

    // 错误行:导出时直接写了函数名但未调用
    exports.show_user_entries; // 这里只是声明,未赋值或调用
    

    这会导致控制器中 show_user_entries 方法未正确导出,当路由引用该函数时(如 router.get("/posts/:author", controllers.show_user_entries)),实际获取到的是 undefined,从而触发上述报错。

    解决方案

    方案一:修复控制器方法的导出

    问题根源:控制器中 show_user_entries 方法未正确导出,仅声明了函数名但未作为属性挂载到 exports 对象上。
    修复步骤

    1. 删除多余的空导出语句 exports.show_user_entries;
    2. 确保所有控制器方法均通过 exports 正确暴露,例如:
      // 正确导出示例(控制器文件 guestbookControllers.js)
      exports.show_user_entries = function (req, res) { // 添加 = 赋值
      let user = req.params.author; 
      dbDAO
       .getUserEntries(user)
       .then((list) => {
         res.render("entries", { title: user + " Guestbook", entries: list });
       })
       .catch((err) => { console.log("err:", err); }); 
      };
      

    方案二:检查路由引用的控制器方法是否存在

    问题场景:若控制器方法名称拼写错误或路径引用错误,也会导致获取到 undefined
    排查步骤

    1. 确认路由文件(guestBookRoutes.js)中引用的控制器方法名称与实际导出的名称一致。
      • 例如,若控制器导出的是 showUserEntries(驼峰命名),但路由中写的是 show_user_entries(下划线命名),会导致引用失败。
    2. 检查路由文件的引入路径是否正确:
      // 路由文件中确保引入路径正确
      const { entries_list, show_user_entries } = require("../controllers/guestbookControllers"); // 确保解构名称正确
      

    方案三:调试路由处理函数是否可访问

    操作步骤
    在路由文件中添加日志,验证控制器方法是否正确导入:

    // 路由文件开头添加调试日志
    const controllers = require("../controllers/guestbookControllers");
    console.log("controllers.show_user_entries:", controllers.show_user_entries); // 输出是否为函数
    

    若输出 undefined,说明控制器中该方法未正确导出,需返回方案一修复导出语句。

    最优方案:方案一(修复控制器导出)

    原因

    • 该问题直接由控制器方法未正确导出导致,是最根本的代码逻辑错误。
    • 其他方案(如检查路由引用)需依赖方案一的正确性,因此优先修复导出语句。

    代码修复示例

    // 原错误代码(控制器文件末尾)
    exports.show_user_entries; // 错误,仅声明未赋值
    exports.about_page = function (req, res) { /* ... */ };
    
    // 修正后
    exports.show_user_entries = function (req, res) { // 添加 = 赋值
      let user = req.params.author; 
      // ... 原方法逻辑 ...
    };
    exports.about_page = function (req, res) { /* ... */ };
    

    完成修复后,重新运行项目,观察控制台是否仍有类似报错。如果还有其他问题(如数据库连接、视图路径等),可以继续留言,我会帮你进一步排查。请楼主采纳,如有疑问欢迎随时提问!

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

问题事件

  • 系统已结题 7月8日
  • 已采纳回答 6月30日
  • 创建了问题 5月26日