教程 express tutorial

博客系统

在本章节中,我们将使用 Express 创建一个简单的博客系统。我们将覆盖从数据库设计、路由创建、文章发布、用户注册与登录、评论功能等多方面的内容。通过实践这一项目,您将对 Express 开发有更深入的了解,并学会如何处理实际项目中的常见功能需求。

1. 项目概述

本博客系统将包含以下基本功能:

  1. 用户身份验证:允许用户注册、登录和退出。
  2. 文章管理:支持用户发布、编辑、删除和查看文章。
  3. 评论功能:允许用户对文章进行评论。
  4. 前后端分离:前端可以通过 API 获取数据并展示。

2. 数据库设计

我们将使用 MongoDB 来存储数据,并通过 Mongoose 来操作数据库。博客系统的主要数据模型如下:

2.1 用户模型

用户模型包含以下字段:

  • username: 用户名,唯一。
  • email: 用户邮箱,唯一。
  • password: 用户密码,哈希存储。
js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');

const userSchema = new mongoose.Schema({
  username: { type: String, required: true, unique: true },
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true },
});

userSchema.methods.hashPassword = async function(password) {
  const salt = await bcrypt.genSalt(10);
  this.password = await bcrypt.hash(password, salt);
};

userSchema.methods.comparePassword = async function(password) {
  return bcrypt.compare(password, this.password);
};

const User = mongoose.model('User', userSchema);

module.exports = User;

`

2.2 文章模型

文章模型包含以下字段:

  • title: 文章标题。
  • content: 文章内容。
  • author: 文章作者,引用用户模型。
  • createdAt: 创建时间。
js
const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
  title: { type: String, required: true },
  content: { type: String, required: true },
  author: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
  createdAt: { type: Date, default: Date.now },
});

const Post = mongoose.model('Post', postSchema);

module.exports = Post;

2.3 评论模型

评论模型包含以下字段:

  • post: 关联文章。
  • author: 评论作者。
  • content: 评论内容。
  • createdAt: 创建时间。
js
const mongoose = require('mongoose');

const commentSchema = new mongoose.Schema({
  post: { type: mongoose.Schema.Types.ObjectId, ref: 'Post', required: true },
  author: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
  content: { type: String, required: true },
  createdAt: { type: Date, default: Date.now },
});

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

module.exports = Comment;

3. 用户身份验证

我们将使用 JWT 来进行用户身份验证。当用户登录时,系统将生成一个 JWT 令牌,客户端将这个令牌保存在本地并用于后续请求的身份验证。

3.1 用户注册

用户注册时,需要提供用户名、邮箱和密码。密码将被哈希后存储在数据库中。

js
const express = require('express');
const bcrypt = require('bcrypt');
const User = require('../models/User');
const jwt = require('jsonwebtoken');

const router = express.Router();

router.post('/register', async (req, res) => {
  const { username, email, password } = req.body;
  
  try {
    const user = new User({ username, email });
    await user.hashPassword(password);
    await user.save();
    res.status(201).json({ message: 'User registered successfully' });
  } catch (err) {
    res.status(400).json({ message: 'Error registering user', error: err });
  }
});

module.exports = router;

3.2 用户登录

用户登录时,我们将验证其邮箱和密码,若验证成功,生成 JWT 令牌并返回给客户端。

js
router.post('/login', async (req, res) => {
  const { email, password } = req.body;

  try {
    const user = await User.findOne({ email });
    if (!user) {
      return res.status(404).json({ message: 'User not found' });
    }

    const isMatch = await user.comparePassword(password);
    if (!isMatch) {
      return res.status(400).json({ message: 'Invalid password' });
    }

    const token = jwt.sign({ userId: user._id }, 'secretkey', { expiresIn: '1h' });
    res.json({ token });
  } catch (err) {
    res.status(500).json({ message: 'Error logging in', error: err });
  }
});

3.3 验证 JWT 令牌

在需要身份验证的 API 路由中,使用 JWT 令牌进行身份验证。

js
const jwt = require('jsonwebtoken');

const authenticate = (req, res, next) => {
  const token = req.header('Authorization');
  if (!token) {
    return res.status(401).json({ message: 'No token provided' });
  }

  try {
    const decoded = jwt.verify(token, 'secretkey');
    req.userId = decoded.userId;
    next();
  } catch (err) {
    res.status(401).json({ message: 'Invalid token' });
  }
};

module.exports = authenticate;

4. 文章功能

4.1 创建文章

用户可以创建文章,输入标题和内容后保存到数据库。

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

router.post('/posts', authenticate, async (req, res) => {
  const { title, content } = req.body;
  
  try {
    const post = new Post({
      title,
      content,
      author: req.userId,
    });

    await post.save();
    res.status(201).json({ message: 'Post created successfully', post });
  } catch (err) {
    res.status(500).json({ message: 'Error creating post', error: err });
  }
});

4.2 获取所有文章

用户可以查看所有文章的列表。

js
router.get('/posts', async (req, res) => {
  try {
    const posts = await Post.find().populate('author', 'username').exec();
    res.json(posts);
  } catch (err) {
    res.status(500).json({ message: 'Error fetching posts', error: err });
  }
});

4.3 编辑文章

用户可以编辑自己发布的文章。

js
router.put('/posts/:id', authenticate, async (req, res) => {
  const { title, content } = req.body;
  try {
    const post = await Post.findById(req.params.id);
    if (!post) {
      return res.status(404).json({ message: 'Post not found' });
    }

    if (post.author.toString() !== req.userId) {
      return res.status(403).json({ message: 'Unauthorized' });
    }

    post.title = title || post.title;
    post.content = content || post.content;
    await post.save();

    res.json({ message: 'Post updated successfully', post });
  } catch (err) {
    res.status(500).json({ message: 'Error updating post', error: err });
  }
});

5. 评论功能

5.1 创建评论

用户可以对文章进行评论,评论将与文章关联。

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

router.post('/posts/:postId/comments', authenticate, async (req, res) => {
  const { content } = req.body;

  try {
    const post = await Post.findById(req.params.postId);
    if (!post) {
      return res.status(404).json({ message: 'Post not found' });
    }

    const comment = new Comment({
      post: post._id,
      author: req.userId,
      content,
    });

    await comment.save();
    res.status(201).json({ message: 'Comment added successfully', comment });
  } catch (err) {
    res.status(500).json({ message: 'Error adding comment', error: err });
  }
});

5.2 获取评论

获取某篇文章下的所有评论。

js
router.get('/posts/:postId/comments', async (req, res) => {
  try {
    const comments = await Comment.find({ post: req.params.postId }).populate('author', 'username').exec();
    res.json(comments);
  } catch (err) {
    res.status(500).json({ message: 'Error fetching comments', error: err });
  }
});

6. 总结

通过本章节的

实现,您已经掌握了如何使用 Express 构建一个简单的博客系统。您学会了如何处理用户身份验证、文章发布、评论功能等常见需求,并了解了如何设计数据库模型和实现基本的 CRUD 操作。在此基础上,您可以扩展更多功能,如文章分类、搜索、分页等,进一步完善您的博客系统。