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

开发Facebook克隆项目时发送好友请求功能报错求助

问题描述

我正在开发一个Facebook克隆项目,最近在实现好友请求逻辑时遇到了困难,希望完成以下功能:

  • 用户在他人个人主页点击“发送好友请求”按钮后,请求能成功提交并进入pending状态
  • 对方查看好友请求时,能看到所有pending请求并选择接受或拒绝
现有代码

FriendRequest 数据模型

const friendRequestSchema = new mongoose.Schema({
  sender: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "User",
    required: true,
  },
  receiver: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "User",
    required: true,
  },
  status: {
    type: String,
    enum: ["pending", "accepted", "rejected"],
    default: "pending",
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
});

发送好友请求按钮表单

<form action="/friend-requests" method="POST">
  <input type="hidden" name="sender" value="<%= id %>" />
  <input type="hidden" name="receiver" value="<%= user._id %>" />
  <button type="submit">发送好友请求</button>
</form>

处理好友请求的后端路由

// POST route to handle the friend request submission
router.post("/friend-requests", (req, res) => {
  const { sender, receiver } = req.body;

  // Create a new FriendRequest instance
  const newRequest = new FriendRequest({
    sender,
    receiver,
  });

  // Save the friend request to the database
  newRequest
    .save()
    .then(() => {
      // Redirect to a success page or send a response indicating success
      res.send("Friend request sent successfully!");
    })
    .catch((error) => {
      // Handle the error appropriately
      res.status(500).send("Error sending friend request");
    });
});

补充代码

用户个人主页的GET路由

router.get("/:user", function (req, res, next) {
  Status.find({}, "content author createdAt")
    .sort({ title: 1 })
    .exec(function (err, list_status) {
      if (err) {
        return next(err);
      }
      res.render("profile", {
        status_list: list_status,
        id: id,
      });
    });
});

发布状态更新的POST路由

router.post("/:user", [
  // Validate and sanitize fields.
  body("content", "Content must not be empty.")
    .trim()
    .isLength({ min: 1 })
    .escape(),
  // Process request after validation and sanitization.
  (req, res, next) => {
    const errors = validationResult(req);

    const status = new Status({
      content: req.body.content,
      author: req.body.author,
    });

    if (!errors.isEmpty()) {
      async.parallel((err, results) => {
        if (err) {
          return next(err);
        }
        res.render("profile", {
          content: content,
          author: author,
          status,
          errors: errors.array(),
        });
      });
      return;
    }
    status.save((err) => {
      if (err) {
        return next(err);
      }
      res.redirect("/");
    });
  },
]);

User 数据模型

const userSchema = new mongoose.Schema({
  firstname: String,
  lastname: String,
  username: {
    type: String,
    unique: true,
    required: [true, "can't be blank"],
    match: [/^[a-zA-Z0-9]+$/, "is invalid"],
    index: true,
  },
  email: {
    type: String,
    lowercase: true,
    unique: true,
    required: [true, "can't be blank"],
    match: [/\S+@\S+\.\S+/, "is invalid"],
    index: true,
  },
  password: String,
  email: String,
  createdAt: { type: Date, default: Date.now },
  friends: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: "User",
    },
  ],
});
问题修复与功能完善方案

1. 修复发送请求的逻辑漏洞

当前POST路由缺少重复请求校验和好友关系检查,修改如下:

router.post("/friend-requests", async (req, res) => {
  try {
    const { sender, receiver } = req.body;
    
    // 检查是否已存在相同的pending请求
    const existingRequest = await FriendRequest.findOne({
      sender,
      receiver,
      status: "pending"
    });
    
    if (existingRequest) {
      return res.status(400).send("好友请求已发送");
    }
    
    // 检查双方是否已是好友
    const senderUser = await User.findById(sender);
    if (senderUser.friends.includes(receiver)) {
      return res.status(400).send("你们已经是好友了");
    }

    const newRequest = new FriendRequest({ sender, receiver });
    await newRequest.save();
    res.redirect(`/${receiver}`); // 发送后跳回对方主页更合理
  } catch (error) {
    console.error(error);
    res.status(500).send("发送好友请求失败");
  }
});

2. 实现接收方查看pending请求的路由

添加GET路由用于获取当前用户的待处理好友请求:

router.get("/friend-requests/pending", async (req, res) => {
  try {
    // 假设当前登录用户ID存在于req.user._id(需确保登录中间件已配置)
    const pendingRequests = await FriendRequest.find({
      receiver: req.user._id,
      status: "pending"
    }).populate("sender", "firstname lastname username"); // 关联查询发送方信息

    res.render("friend-requests", { pendingRequests });
  } catch (error) {
    console.error(error);
    res.status(500).send("加载好友请求失败");
  }
});

3. 实现接受/拒绝请求的路由

添加POST路由处理请求的状态变更:

router.post("/friend-requests/:id/action", async (req, res) => {
  try {
    const { id } = req.params;
    const { action } = req.body; // action 为 "accept" 或 "reject"

    const request = await FriendRequest.findById(id);
    if (!request || request.receiver.toString() !== req.user._id.toString()) {
      return res.status(403).send("无权操作此请求");
    }

    if (action === "accept") {
      // 更新请求状态为已接受
      request.status = "accepted";
      await request.save();

      // 双向添加好友关系
      await User.findByIdAndUpdate(
        req.user._id,
        { $push: { friends: request.sender } }
      );
      await User.findByIdAndUpdate(
        request.sender,
        { $push: { friends: req.user._id } }
      );
    } else if (action === "reject") {
      request.status = "rejected";
      await request.save();
    }

    res.redirect("/friend-requests/pending");
  } catch (error) {
    console.error(error);
    res.status(500).send("处理请求失败");
  }
});

4. 完善个人主页的渲染逻辑

修复id变量未定义问题,同时添加关系状态判断:

router.get("/:user", async function (req, res, next) {
  try {
    const targetUser = await User.findById(req.params.user);
    if (!targetUser) {
      return res.status(404).send("用户不存在");
    }

    const list_status = await Status.find({ author: req.params.user })
      .sort({ createdAt: -1 }) // 按时间倒序排列状态
      .populate("author", "username");

    // 检查当前用户与目标用户的关系:是否是好友、是否已发送请求
    const currentUserId = req.user?._id;
    const isFriend = currentUserId && targetUser.friends.includes(currentUserId);
    const hasPendingRequest = currentUserId && await FriendRequest.findOne({
      sender: currentUserId,
      receiver: targetUser._id,
      status: "pending"
    });

    res.render("profile", {
      status_list: list_status,
      user: targetUser,
      currentUserId,
      isFriend,
      hasPendingRequest
    });
  } catch (err) {
    return next(err);
  }
});

5. 更新前端表单逻辑

根据关系状态动态显示按钮:

<% if (currentUserId && currentUserId.toString() !== user._id.toString()) { %>
  <% if (isFriend) { %>
    <button disabled>已添加好友</button>
  <% } else if (hasPendingRequest) { %>
    <button disabled>请求已发送</button>
  <% } else { %>
    <form action="/friend-requests" method="POST">
      <input type="hidden" name="sender" value="<%= currentUserId %>" />
      <input type="hidden" name="receiver" value="<%= user._id %>" />
      <button type="submit">发送好友请求</button>
    </form>
  <% } %>
<% } %>

6. 好友请求列表页面模板(friend-requests.ejs)

<h1>待处理好友请求</h1>
<% if (pendingRequests.length === 0) { %>
  <p>暂无待处理请求</p>
<% } else { %>
  <% pendingRequests.forEach(request => { %>
    <div>
      <p><%= request.sender.firstname %> <%= request.sender.lastname %> (@<%= request.sender.username %>)</p>
      <form action="/friend-requests/<%= request._id %>/action" method="POST">
        <input type="hidden" name="action" value="accept" />
        <button type="submit">接受</button>
      </form>
      <form action="/friend-requests/<%= request._id %>/action" method="POST" style="display:inline;">
        <input type="hidden" name="action" value="reject" />
        <button type="submit">拒绝</button>
      </form>
    </div>
  <% }) %>
<% } %>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 04:37:01