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

followUsers函数未将ID加入followers/followings数组且无请求响应的修复

用户关注功能异常排查与修复

问题概述

基于Node.js + MongoDB的项目中,用户关注功能失效:完成JWT认证拿到token后调用followUsers接口,既没有收到响应,数据库里的followers和followings数组也没更新。

原代码问题排查

1. verifyToken.js 语法与逻辑错误

  • if语句未闭合大括号,导致代码结构混乱
  • next() 被错误嵌套在else分支内,正常认证后无法触发后续中间件
  • 异常捕获后仅打印错误,未调用next(error)或返回错误响应,导致请求挂起

2. User.js Schema语法错误

  • timestamps: true 被错误放在了schema字段对象内部,应该作为第二个参数传入mongoose.Schema
  • fullname设置unique: true不合理,会导致重名用户无法注册

3. userRoutes.js 路由路径错误

  • 好友列表路由缺少前置斜杠,应为 /friends/:userId,否则无法匹配请求

4. controllers/User.js 语法与逻辑错误

  • followUsers函数中if语句未闭合大括号,代码结构错误
  • 逻辑判断错误:else分支提示"已关注自己",实际应该是"已关注该用户"
  • 未处理用户不存在的情况(比如findById返回null)
  • unfollowUsers函数中字段名错误:使用了follower/following,但Schema定义的是followers/followings
  • 存在语法缺失(比如res.status后缺少分号,if/else闭合问题)

修复后的完整代码

1. verifyToken.js

import jwt from 'jsonwebtoken';

export const verifyToken = (req, res, next) => {
  try {
    const token = req.headers.authorization?.split(" ")[1];
    const isCustomAuth = token?.length < 500;

    let decodedData;

    if (token && isCustomAuth) {
      decodedData = jwt.verify(token, process.env.JWT_KEY);
      req.user = decodedData?.id;
    } else {
      decodedData = jwt.decode(token);
      req.user = decodedData?.sub;
    }
    // 认证成功后触发后续中间件
    next();
  } catch (error) {
    console.log(error);
    // 传递错误到全局错误处理中间件
    next(error);
  }
};

2. User.js

import mongoose from "mongoose";

export const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true,
    max: 50,
  },
  username: {
    type: String,
    required: true,
    unique: true,
    max: 20,
  },
  fullname: {
    type: String,
    required: true,
    max: 50,
  },
  password: {
    type: String,
    required: true,
    min: 6,
  },
  followings: {
    type: Array,
    default: [],
  },
  followers: {
    type: Array,
    default: [],
  },
  profilePicture: {
    type: String,
    default: "",
  },
  coverPicture: {
    type: String,
    default: "",
  },
  location: {
    type: String,
    max: 30,
  },
  website: {
    type: String,
    default: "",
    max: 100,
  },
  dateOfBirth: {
    type: String,
    required: true,
    match: /^\d{2}[-]\d{2}[-]\d{4}$/,
  },
}, { timestamps: true });

export default mongoose.model('Users', userSchema);

3. userRoutes.js

import express from 'express';
import { verifyToken } from '../middleware/verifyToken.js';
import { update, deleteUser, getUser, friendsList, followUsers, unfollowUsers } from '../controllers/User.js';

const router = express.Router();

// 更新用户
router.put('/:id', verifyToken, update);

// 删除用户
router.delete('/:id', verifyToken, deleteUser);

// 获取用户
router.get('/find/:id', getUser);

// 好友列表
router.get('/friends/:userId', friendsList);

// 关注用户
router.put('/follow/:id', verifyToken, followUsers);

// 取消关注用户
router.put('/unfollow/:id', verifyToken, unfollowUsers);

export default router;

4. controllers/User.js

import User from '../models/User.js';
import { createError } from '../utils/error.js';

export const followUsers = async (req, res, next) => {
  if (req.params.id !== req.user?.id) {
    try {
      const targetUser = await User.findById(req.params.id);
      const currentUser = await User.findById(req.user?.id);

      if (!targetUser || !currentUser) {
        return next(createError(404, "用户不存在"));
      }

      if (!targetUser.followers.includes(req.user?.id)) {
        await targetUser.updateOne({ $push: { followers: req.user?.id } });
        await currentUser.updateOne({ $push: { followings: req.params.id } });
        res.status(200).json("已成功关注该用户");
      } else {
        return next(createError(403, "你已经关注了该用户"));
      }
    } catch (error) {
      console.error(error);
      next(error);
    }
  } else {
    return next(createError(403, "无法关注自己的账号"));
  }
};

export const unfollowUsers = async (req, res, next) => {
  if (req.params?.id !== req.user?.id) {
    try {
      const targetUser = await User.findById(req.params.id);
      const currentUser = await User.findById(req.user.id);

      if (!targetUser || !currentUser) {
        return next(createError(404, "用户不存在"));
      }

      if (targetUser.followers.includes(req.user?.id)) {
        await targetUser.updateOne({ $pull: { followers: req.user?.id } });
        await currentUser.updateOne({ $pull: { followings: req.params.id } });
        res.status(200).json("已成功取消关注");
      } else {
        return next(createError(403, "你还未关注该用户"));
      }
    } catch (error) {
      next(error);
    }
  } else {
    return next(createError(403, "无法取消关注自己的账号"));
  }
};

额外优化建议

  1. 使用事务确保关注/取消关注操作的原子性:如果其中一个更新失败,另一个也应该回滚,避免数据不一致
  2. 将数组字段改为关联ObjectId数组,提升数据规范性:
followings: [{
  type: mongoose.Schema.Types.ObjectId,
  ref: 'Users',
  default: []
}],
followers: [{
  type: mongoose.Schema.Types.ObjectId,
  ref: 'Users',
  default: []
}]
  1. 添加请求参数校验(比如使用joi或express-validator),避免传入无效的用户ID

内容的提问来源于stack exchange,提问作者Mehmet Ali Koyuncuoglu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 09:23:10