开发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
相关产品推荐
相关产品推荐

