博客项目全栈开发 API接口 第四章:评论系统

前言

默认大家已经看过前面的文章,并成功完成前面步骤。这章主要写评论的CURD功能api的开发

评论功能

创建评论模型models/Comment.js

const mongoose = require('mongoose');

const commentSchema = new mongoose.Schema({
    content: {
      type: String,
      required: [true, '评论内容不能为空'],
      trim: true,
      maxlength: [1000, '评论最多1000个字符']
    },
    post: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Post',
      required: [true, '评论必须关联到文章']
    },
    author: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'User',
      required: [true, '评论必须关联到用户']
    },
    parentComment: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Comment'
    },
    status: {
      type: String,
      enum: ['active', 'deleted'],
      default: 'active'
    }
  }, {
    timestamps: true
  });

const Comment = mongoose.model('Comment', commentSchema);
module.exports = Comment;

创建评论控制器controllers/commentController.js

const Comment = require('../models/Comment');
const Post = require('../models/Post');

// 创建评论
const createComment = async (req, res) => {
  try {
    const { content, postId, parentCommentId } = req.body;
    
    // 检查文章是否存在
    const post = await Post.findById(postId);
    if (!post) {
      return res.status(404).json({ message: '文章不存在' });
    }

    // 如果是回复评论,检查父评论是否存在
    if (parentCommentId) {
        // 查找父评论,并确保它属于同一篇文章且未被删除
        const parentComment = await Comment.findOne({
            _id: parentCommentId,
            post: postId,        // 确保父评论属于同一篇文章
            status: 'active'     // 确保父评论未被删除
        });
    
        if (!parentComment) {
            return res.status(404).json({ 
                message: '父评论不存在或已被删除' 
            });
        }
    }

    const comment = await Comment.create({
      content,
      post: postId,
      author: req.user._id,
      parentComment: parentCommentId
    });

    // 填充作者信息
    await comment.populate('author', 'username');
    
    res.status(201).json(comment);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
};

// 获取文章的所有评论
exports.getComments = async (req, res) => {
  try {
    const comments = await Comment.find({ post: req.params.postId })
      .populate('author', 'name')
      .populate('parentComment')
      .sort('-createdAt');
    
    res.json(comments);
  } catch (error) {
    res.status(500).json({ message: '获取评论失败' });
  }
};

// 更新评论
exports.updateComment = async (req, res) => {
  try {
    const comment = await Comment.findById(req.params.id);
    
    if (!comment) {
      return res.status(404).json({ message: '评论不存在' });
    }
    
    // 检查是否是评论作者
    if (comment.author.toString() !== req.user._id.toString()) {
      return res.status(403).json({ message: '没有权限修改此评论' });
    }
    
    comment.content = req.body.content;
    await comment.save();
    
    res.json(comment);
  } catch (error) {
    res.status(500).json({ message: '更新评论失败' });
  }
};

// 删除评论
exports.deleteComment = async (req, res) => {
  try {
    const comment = await Comment.findById(req.params.id);
    
    if (!comment) {
      return res.status(404).json({ message: '评论不存在' });
    }
    
    // 检查是否是评论作者
    if (comment.author.toString() !== req.user._id.toString()) {
      return res.status(403).json({ message: '没有权限删除此评论' });
    }
    
    await comment.remove();
    
    res.json({ message: '评论已删除' });
  } catch (error) {
    res.status(500).json({ message: '删除评论失败' });
  }
};

创建评论路由routes/comment.js

const express = require('express');
const router = express.Router();
const { 
  createComment, 
  getComments, 
  updateComment, 
  deleteComment,
  toggleLike
} = require('../controllers/commentController');
const { protect } = require('../middleware/auth');

// 创建评论(需要登录)
router.post('/', protect, createComment);

// 获取文章的所有评论
router.get('/post/:postId', getComments);

// 更新评论(需要登录)
router.put('/:id', protect, updateComment);

// 删除评论(需要登录)
router.delete('/:id', protect, deleteComment);

// 点赞/取消点赞评论(需要登录)
router.post('/:id/like', protect, toggleLike);

module.exports = router;

app.js注册评论路由

const commentRoutes = require('./routes/comment');
// 使用 commentRoutes 路由
app.use('/api/comments', commentRoutes);

解释下创建方法使用populate问题

// 填充作者信息
await comment.populate('author', 'username');

这里在返回之前加了这行代码。为什么这么做。

  • 前端不需要再单独请求用户信息
  • 一次请求就能获取所有需要的数据
  • 可以指定只返回需要的字段

总结来说,前端要什么我们给什么。省去前端在去调用查询用户信息的接口了。
说的有点模糊,直接上数据把

// 1. 创建评论
const comment = await Comment.create({
    content: "这是一条评论",
    author: req.user._id,
    post: postId
});

// 2. 填充作者信息
await comment.populate('author', 'username');

// 3. 返回给前端
res.json(comment);
// 前端收到的数据:
{
    "_id": "comment123",
    "content": "这是一条评论",
    "author": {
        "_id": "user456",
        "username": "张三"
    },
    "post": "post789",
    "createdAt": "2024-01-01T00:00:00.000Z"
}

// 4. 数据库中实际存储:
{
    "_id": "comment123",
    "content": "这是一条评论",
    "author": "user456",  // 仍然是用户ID
    "post": "post789",
    "createdAt": "2024-01-01T00:00:00.000Z"
}

测试:

token和文章id用之前的方法生成一个

POST /api/comments
Content-Type: application/json
Authorization: Bearer your_token_here

{
    "content": "这是一条评论",
    "postId": "文章ID"
}

返回:

{
    "content": "这是一条评论",
    "post": "6825e0d1ab18230b995814ca",
    "author": {
        "_id": "68234d0c35c1531af74f8b21",
        "username": "测试用户"
    },
    "status": "active",
    "_id": "68260100b2c059a075973668",
    "createdAt": "2025-05-15T14:58:08.489Z",
    "updatedAt": "2025-05-15T14:58:08.489Z",
    "__v": 0
}

获取所有评论

POST http://localhost:3000/api/comments/post/6825e0d1ab18230b995814ca
Content-Type: application/json
Authorization: Bearer your_token_here

返回

{
    "comments": [
        {
            "_id": "68260100b2c059a075973668",
            "content": "这是一条评论",
            "post": "6825e0d1ab18230b995814ca",
            "author": {
                "_id": "68234d0c35c1531af74f8b21",
                "username": "测试用户"
            },
            "status": "active",
            "createdAt": "2025-05-15T14:58:08.489Z",
            "updatedAt": "2025-05-15T14:58:08.489Z",
            "__v": 0,
            "replies": [
                {
                    "_id": "68260250b2c059a07597367a",
                    "content": "这是一条回复评论",
                    "post": "6825e0d1ab18230b995814ca",
                    "author": {
                        "_id": "68234d0c35c1531af74f8b21",
                        "username": "测试用户"
                    },
                    "parentComment": "68260100b2c059a075973668",
                    "status": "active",
                    "createdAt": "2025-05-15T15:03:44.560Z",
                    "updatedAt": "2025-05-15T15:03:44.560Z",
                    "__v": 0
                }
            ]
        }
    ],
    "totalPages": 1,
    "currentPage": 1
}

评论接口完工!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值