【Node.js实战系列】Express 从零搭建博客系统(一)

前言

哈喽,大家好,这里是胖芙,咱这系列的文章主打的就是一个通俗易懂。这个系列的文章会从零开始教你怎么使用 Express.js 去搭建一个能用的博客系统,是真的从零,手把手的那种!!!让你打通 JS 前后端任督二脉!

本系列文章也会收录在公众号《泡芙学前端》中,持续更新中… 欢迎关注

首先要知道我们现在要做个怎样的系统,比如博客能干嘛?

那它的核心功能肯定是对于文章的增删改查嘛,所以我们就有了以下的几个核心接口:

  • 发表博客
  • 分页查看博客列表
  • 根据id查看博客详情
  • 根据id删除博客(软删除)
  • 根据id修改博客内容
  • 上传文件(比如博客内的图片),前期我们先把图片存本地,后面可以改成上传到 OSS

好了,这几个接口搞完了其实博客系统的雏形就已经有了。

但是哎,好像还不太够,还缺点啥?所以接下来我们继续给博客添加功能,比如用户登录注册功能:

  • 用户注册
  • 用户登录

嗯,开始有点内味了,到这里一个基本的博客系统就其实已经可以自己用起来了。

接下来我们就继续锦上添花,比如我们为了让博客能分类,来添加一个标签系统,可以给博客打上分类的标签,比如 JavaScript、Node.js、React 等标签

  • 新增标签
  • 查询标签
  • 删除标签(软删除)

【Node.js实战系列】Express 从零搭建博客系统(一)

到这里为止一个博客的雏形就已经有了,后续可以基于这个雏形来添加更多的功能,比如接入 Redis 缓存进行优化等,不过那都是后话了,我们这里就先把一个雏形从零开始搭建完成。

我们本系列教程只写后端,大家如果有兴趣的话可以自己写一下前端,或者如果后续反馈好,我也可以把前端部分给补上。

废话不多说,直接开始吧。首先我们先来完成基本环境的一个搭建工作哈

1.搭建初始环境

  • 找到一个地方创建目录,比如 express-blog

  • 进入目录,控制台运行 npm init -y

  • 修改 package.json 中的 scripts 字段为下面代码,作为启动入口,推荐安装 nodemon 这个包,当文件变更时它可以自动重启服务

    • npm i nodemon -g 安装到全局

    • "scripts": {
        "start": "nodemon app.js"
      },
      
  • 安装依赖:npm install express sequelize mysql2 body-parser cors bcrypt jsonwebtoken multer

    • express: 我们要用的服务端框架
    • sequelize: 操作数据库的 orm
    • mysql2: 数据库驱动
    • body-parser: 用来解析 request body 的内容
    • cors: 解决跨域
    • bcrypt: 密码加密和解密
    • jsonwebtoken: 登录时生成 token 下发
    • multer: 用于上传文件

上面搞完后,我们就得到了这样的一个 package.json 文件

{
  "name": "expree-blog",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "nodemon app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bcrypt": "^5.1.0",
    "body-parser": "^1.20.2",
    "cors": "^2.8.5",
    "express": "^4.18.2",
    "jsonwebtoken": "^9.0.0",
    "multer": "1.4.5-lts.1",
    "mysql2": "^3.3.3",
    "sequelize": "^6.31.1"
  }
}

到这里我们就完成了第一步工作,接下来我们就要创建目录结构了,最终项目完成时的目录结构长这个样子

expree-blog                   // 根目录
├── app.js                    // 项目主入口
├── config                    // 数据库配置
│   └── database.js
├── controllers               // 控制器,写逻辑的地方
│   ├── authController.js     // 登录注册控制器
│   ├── blogController.js     // 博客控制器
│   ├── fileController.js     // 文件控制器
│   └── tagController.js      // 标签控制器
├── middleware                // 中间件
│   ├── authMiddleware.js     // 鉴权中间件
│   └── fileMiddleware.js     // 文件处理中间件
├── models                    // 数据库模型
│   ├── Blog.js
│   ├── Tag.js
│   └── User.js
├── package.json
├── routes                    // 路由
│   ├── auth.js
│   ├── blogs.js
│   ├── file.js
│   └── tags.js
└── tempFiles                 // 存储上传的文件

2.数据库配置

这里可以启动自己的本地 mysql 数据库,然后填入对应信息即可,不知道怎么安装 mysql 的可以网上查下相关教程,可以使用 docker 安装,或者直接去官网下载安装包然后装到本地。我自己的话是用的 Docker

const { Sequelize } = require("sequelize");

const sequelize = new Sequelize(
  // 数据库名称
  "your_database_name",
  // 数据库用户名
  "your_username",
  // 数据库密码
  "your_password",
  {
    // 如果是远程数据库,可以填写 ip 地址
    host: "localhost",
    dialect: "mysql",
  }
);

module.exports = sequelize;

当数据库启动起来之后,改一下上面的参数即可

3.构建数据库模型

现在我们要在 models/ 目录下创建下面这几个模型文件:

  • User.js:用户模型
  • Blog.js:博客模型
  • Tag.js:标签模型

然后在各个模型中去定义相应的字段:

用户模型

// models/User.js

const { DataTypes, Model } = require("sequelize");
const sequelize = require("../config/database");

class User extends Model {}

User.init(
  {
    username: {
      comment: "用户名",
      type: DataTypes.STRING,
      allowNull: false,
      unique: true,
    },
    password: {
      comment: "密码",
      type: DataTypes.STRING,
      allowNull: false,
    },
    nickname: {
      comment: "昵称",
      type: DataTypes.STRING,
      allowNull: false,
    },
    lastOnlineTime: {
      comment: "最后登陆时间",
      type: DataTypes.DATE,
      allowNull: true,
    },
  },
  {
    sequelize,
    modelName: "User",
  }
);

module.exports = User;

博客模型

// models/Blog.js

const { DataTypes, Model } = require("sequelize");
const sequelize = require("../config/database");
const User = require("./User");
const Tag = require("./Tag");

class Blog extends Model {}

Blog.init(
  {
    title: {
      comment: "标题",
      type: DataTypes.STRING,
      allowNull: false,
    },
    content: {
      comment: "博客内容",
      type: DataTypes.TEXT,
      allowNull: false,
    },
    coverImage: {
      comment: "封面图",
      type: DataTypes.STRING,
    },
    isDeleted: {
      comment: "是否已经删除",
      type: DataTypes.BOOLEAN,
      allowNull: false,
      defaultValue: false,
    },
  },
  {
    sequelize,
    modelName: "Blog",
  }
);

// 一篇博客可以对应多个标签
// 一个标签也可以对应到多篇博客

// 博客与标签的关联关系
Blog.belongsToMany(Tag, {
  through: "Blog_Tag",
  as: "tags",
});

// 标签和博客的关联关系
Tag.belongsToMany(Blog, {
  through: "Blog_Tag",
  as: "blogs",
});

// 一篇博客只能属于一个用户
// 一个用户可以拥有多篇博客

// 博客与用户的关联关系
Blog.belongsTo(User, { foreignKey: "userId", as: "user" });
User.hasMany(Blog, { foreignKey: "userId", as: "user" });

module.exports = Blog;

标签模型

// models/Tag.js

const { DataTypes, Model } = require("sequelize");
const sequelize = require("../config/database");
class Tag extends Model {}

Tag.init(
  {
    name: {
      comment: "标签名称",
      type: DataTypes.STRING,
      allowNull: false,
      unique: true,
    },
    isDeleted: {
      comment: "是否已经删除",
      type: DataTypes.BOOLEAN,
      allowNull: false,
      defaultValue: false,
    },
  },
  {
    sequelize,
    modelName: "Tag",
  }
);

module.exports = Tag;

4.初始化主入口

const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const path = require("path");
const authRoutes = require("./routes/auth");
const blogRoutes = require("./routes/blogs");
const tagRoutes = require("./routes/tags");
const fileRoutes = require("./routes/file");
const sequelize = require("./config/database");

const app = express();

// 中间件
// 用来解析 post body x-www-form-urlencoded 格式数据
app.use(bodyParser.urlencoded({ extended: false }));
// 用来解析 post body json 格式数据
app.use(bodyParser.json());
// 处理跨域
app.use(cors());
// 提供静态文件访问
app.use("/tempFiles", express.static(path.join(__dirname, "tempFiles")));

// 路由
app.use("/auth", authRoutes);
app.use("/blogs", blogRoutes);
app.use("/tags", tagRoutes);
app.use("/upload", fileRoutes);

// 数据库同步
sequelize
  .sync()
  .then(() => {
    console.log("Database synced");
  })
  .catch((error) => {
    console.error("Error syncing database:", error);
  });

// 启动服务器
const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

5. 初始化路由

这里我们采用 Restful 风格的路由

登录注册路由

// routes/auth.js

const express = require('express');
const router = express.Router();
const authController = require('../controllers/authController');

router.post('/register', authController.register);
router.post('/login', authController.login);

module.exports = router;

博客路由

// routes/blogs.js

const express = require('express');
const router = express.Router();
const blogController = require('../controllers/blogController');

// 创建博客
router.post('/create', blogController.createBlog);
// 查询博客列表
router.get('/query', blogController.getBlogList);
// 根据 id 查询博客详情
router.get('/query/:id', blogController.getBlogById);
// 根据 id 修改博客
router.patch('/update/:id', blogController.updateBlog);
// 根据 id 删除博客
router.delete('/delete/:id', blogController.deleteBlog);

module.exports = router;

标签路由

// routes/tags.js

const express = require('express');
const router = express.Router();
const tagController = require('../controllers/tagController');

// 创建标签
router.post('/create', tagController.createTag);
// 查询所有标签
router.get('/query', tagController.getTags);
// 根据 id 删除标签
router.delete('/delete/:id', tagController.deleteTag);

module.exports = router;

文件路由

// routes/file.js
const express = require("express");
const router = express.Router();
const fileController = require("../controllers/fileController");

// 目前只有一个上传文件接口
router.post("/file", fileController.uploadFile);

module.exports = router;

6.初始化控制器

登录注册

// 注册
async function register(req, res) {
  // todo
}

// 登录
async function login(req, res) {
  // todo
}

module.exports = { register, login };

博客

async function createBlog(req, res) {
  // todo
}

async function getBlogList(req, res) {
  // todo
}

async function getBlogById(req, res) {
  // todo
}

async function updateBlog(req, res) {
  // todo
}

async function deleteBlog(req, res) {
  // todo
}

module.exports = {
  createBlog,
  getBlogList,
  getBlogById,
  updateBlog,
  deleteBlog,
};

标签

async function createTag(req, res) {
  // todo
}

async function getTags(req, res) {
  // todo
}

async function updateTag(req, res) {
  // todo
}

async function deleteTag(req, res) {
  // todo
}

module.exports = { createTag, getTags, updateTag, deleteTag };

上传文件

async function uploadFile(req, res) {
  // todo
}

module.exports = { uploadFile };

小结

以上就是关于 Express 从零搭建博客系统的初始化相关的全部内容了~

下一篇文章我们将完成登录和注册功能~,来实现 token 的下发和校验功能

如果想要实时收到文章的更新,欢迎关注公众号《泡芙学前端》,同步更新文章中~

原文链接:https://juejin.cn/post/7240342069997715511 作者:PuffMeow

(0)
上一篇 2023年6月4日 上午10:20
下一篇 2023年6月4日 上午10:32

相关推荐

发表回复

登录后才能评论