基于NodeJs+Express+MySQL 实现的个人博客项目

目录

  • 项目简介:
    • 主要功能
    • 技术栈
  • 项目目标:
  • 效果展示:
  • 一. 创建项目并初始化
  • 二. 项目初始化
  • 三. 安装项目所需要的包
  • 四. 创建需要的数据库
  • 五. 编写app.js
  • 六. 创建前端页面

项目简介:

这个项目是一个基于Node.js和Express框架构建的简易博客系统,用户可以在该系统中进行注册、登录、发布文章、查看文章、删除文章以及留言等操作。项目利用MySQL数据库存储用户和文章数据,并通过前端页面提供用户友好的交互界面。以下是项目的主要功能简介:

主要功能

  1. 用户注册与登录

    • 用户可以通过提供用户名和密码进行注册,注册后凭借用户名和密码登录系统。 * 系统会对用户密码进行加密储存,提高安全性。
  2. 文章管理

    • 登录用户可以发布新的文章,包括输入标题和内容。
    • 用户可以查看所有已发布的文章列表,并可以通过文章ID查找具体文章的详细内容。
    • 登录用户可以删除自己发布的文章。
  3. 留言板功能

    • 用户可以在留言板上发布留言和查看其他用户的留言。
    • 留言也可以被删除。
  4. 文章与留言分页

    • 在文章列表和留言板上支持分页功能,每页显示固定数量的内容,用户可翻页查看更多内容。
  5. 用户会话管理

    • 系统通过Session管理用户登录状态,确保用户的安全性与隐私。
  6. 前端页面

    • 采用HTML、CSS和jQuery构建响应式用户界面,前端页面包括登录页、注册页、文章发布页、文章详情页、留言板等。

技术栈

  • 后端:Node.js, Express, MySQL
  • 前端:HTML, CSS, JavaScript (jQuery)
  • 其他:bcrypt用于密码安全,Axios用于进行API请求。

项目目标:

该项目的目标是提供一个简易的博客系统,使得用户能够方便地进行文章管理和互动留言,增强用户之间的联系与交流。适合于学习Node.js、Express和前后端交互的初学者。

效果展示:

登录页

在这里插入图片描述

注册页

在这里插入图片描述

首页

在这里插入图片描述

文章详情页

在这里插入图片描述

添加文章页

在这里插入图片描述

留言页

在这里插入图片描述

修改和删除文章页

在这里插入图片描述

修改文章页

在这里插入图片描述

一. 创建项目并初始化

项目结构

在这里插入图片描述

二. 项目初始化

//进入项目文件夹执行命令初始化
npm init -y

三. 安装项目所需要的包

npm i bcrypt body-parser cors express express-session mysql

在这里插入图片描述

四. 创建需要的数据库

创建数据库和表用户表、文章表、留言表

/*
 Navicat Premium Dump SQL

 Source Server         : weblog2
 Source Server Type    : MySQL
 Source Server Version : 80037 (8.0.37)
 Source Host           : localhost:3306
 Source Schema         : notebook

 Target Server Type    : MySQL
 Target Server Version : 80037 (8.0.37)
 File Encoding         : 65001

 Date: 17/12/2024 19:15:06
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for article
-- ----------------------------
DROP TABLE IF EXISTS `article`;
CREATE TABLE `article`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
  `title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
  `content` varchar(9999) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
  `time` varchar(99) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 84 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for leaving
-- ----------------------------
DROP TABLE IF EXISTS `leaving`;
CREATE TABLE `leaving`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
  `content` varchar(9999) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
  `time` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 54 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `username` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
  `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_bin ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

五. 编写app.js

连接数据库和所有后端接口代码都在这里,都写在app.js

//导入express模块
const express = require('express')
const bodyParser = require('body-parser')
const session = require('express-session')
//导入 mysql 模块
const mysql = require('mysql')
const bcrypt = require('bcrypt')
const cors = require('cors')
const fs = require('fs')

//建立与 MySQL 数据库的连接关系
const db = mysql.createPool({
    host: 'localhost',
    user: 'root',
    password: 'root',
    database: 'notebook',
})

//创建app应用
const app = express()

app.use(cors(
    {
        origin: 'http://localhost',
        credentials: true
    }
))

//使用session中间件
app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true,
    cookie: { maxAge: 1000 * 60 * 60 * 24 }
}))

//定义post传递的格式
app.use(express.static('./pages'))
// 静态资源目录
app.use('/public', express.static('./public'))
//使用body-parser中间件
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())

//登录接口
app.post('/api/login', (req, res) => {
    const sqlStr = 'SELECT * FROM user WHERE username = ?'
    const params = [req.body.username]

    db.query(sqlStr, params, (err, results) => {
        if (err) {
            console.error(err.message)
            return res.status(500).send({ message: '服务器内部错误' })
        }

        if (results.length === 0) {
            return res.status(400).send({ message: '该用户不存在' })
        }

        // 验证密码
        bcrypt.compare(req.body.password, results[0].password, (err, validPassword) => {
            if (err) {
                console.error(err.message)
                return res.status(500).send({ message: '服务器内部错误' })
            }

            if (!validPassword) {
                return res.status(401).send({ message: '密码错误' })
            }

            req.session.user = req.body.username;
            req.session.islogin = true;
            res.status(200).send({ message: '登录成功' })
        })
    })
})

//注册接口
app.post('/api/register', (req, res) => {
    const sqlStr = 'SELECT * FROM user WHERE username = ?'
    const params = [req.body.username]
    db.query(sqlStr, params, (err, results) => {
        if (err) {
            console.error(err.message)
            return res.status(500).send({ message: '服务器内部错误' })
        }

        if (results.length > 0) {
            return res.status(400).send({ message: '该用户已存在' })
        }

        // 对密码进行哈希处理
        bcrypt.hash(req.body.password, 10, (err, hashedPassword) => {
            if (err) {
                console.error(err.message)
                return res.status(500).send({ message: '服务器内部错误' })
            }

            const sqlInsert = 'INSERT INTO user (username, password) VALUES (?, ?)'
            db.query(sqlInsert, [req.body.username, hashedPassword], (err, results) => {
                if (err) {
                    console.error(err.message);
                    return res.status(500).send({ message: '服务器内部错误' })
                }

                res.status(201).send({ message: '注册成功' })
            })
        })
    })
})

// 获取用户姓名接口
app.get('/api/username', (req, res) => {
    //从 Session 中获取用户的名称,响应给客户端
    if (req.session.islogin && req.session.user) {
        return res.send({
            status: 200,
            message: '获取用户名成功',
            username: req.session.user // 返回用户名
        })
    }
})

// 退出登录接口
app.post('/api/logout', (req, res) => {
    // 清除session
    req.session.destroy()
    res.send({ status: 200, message: '退出登录成功' })
})

// 获取文章列表接口
app.get('/api/getArticle', (req, res) => {
    const sqlStr = 'SELECT * FROM article';
    db.query(sqlStr, (err, results) => {
        if (err) {
            return res.status(500).send({
                status: 500,
                message: '服务器内部错误'
            });
        }
        res.send({
            status: 200,
            message: '获取文章成功',
            data: results // 将所有文章数据返回
        });
    });
});

// 新增文章接口
app.post('/api/addArticle', (req, res) => {
    // 获取当前时间
    let time = new Date().toLocaleString()

    // 检查请求体中是否包含必要字段
    if (!req.body.title || !req.body.content) {
        return res.status(400).send({
            status: 400,
            message: '标题和内容不能为空',
        })
    }

    const sqlStr = 'INSERT INTO article (username, title, content, time) VALUES (?,?,?,?)'
    const params = [req.body.username, req.body.title, req.body.content, time]
    db.query(sqlStr, params, (err, results) => {
        if (err) {
            console.log(err.message);
            return res.status(500).send({
                status: 500,
                message: '服务器内部错误',
            })
        }

        res.send({
            status: 200,
            message: '新增文章成功',
            data: {
                id: results.insertId,  // 返回新文章的ID
                username: req.session.user,
                title: req.body.title,
                content: req.body.content,
                time: time
            }
        })
    })
})

// 查找文章接口
app.post('/api/search', (req, res) => {
    let time = new Date().toLocaleString()
    const sqlStr = 'SELECT * FROM article WHERE id = ?'
    const params = [req.body.id];
    db.query(sqlStr, params, (err, results) => {
        if (err) {
            console.log(err.message);
            return res.status(500).send({
                status: 500,
                message: '服务器内部错误',
            })
        }

        if (results.length === 0) {
            return res.send({
                status: 404,
                message: '未找到文章',
            })
        }

        res.send({
            status: 200,
            message: '查找文章成功',
            data: results[0]  // 如果按ID查找,返回单个结果
        })
    })
})

// 删除文章接口
app.post('/api/delete', (req, res) => {
    const sqlStr = 'DELETE FROM article WHERE id = ?'
    const params = [req.body.id]
    db.query(sqlStr, params, (err, results) => {
        if (err) {
            console.error(err.message)
            return res.status(500).send({
                status: 500,
                message: '服务器内部错误'
            })
        }

        if (results.affectedRows === 0) {
            return res.status(404).send({
                status: 404,
                message: '未找到该文章'
            })
        }

        res.send({
            status: 200,
            message: '删除文章成功'
        })
    })
})

// 动态获取文章内容接口
app.get('/api/article/:_id', (req, res) => {
    const id = parseInt(req.params._id) // 获取并转为整数
    const sqlStr = 'SELECT * FROM article WHERE id = ?'
    const params = [id]

    db.query(sqlStr, params, (err, results) => {
        if (err) {
            console.error(err.message)
            return res.status(500).send({
                status: 500,
                message: '服务器内部错误'
            })
        }

        if (results.length === 0) {
            return res.status(404).send({
                status: 404,
                message: '未找到该文章'
            })
        }

        // 发送 JSON 格式的响应
        res.send({
            status: 200,
            message: '获取文章成功',
            data: {
                username: results[0].username,
                title: results[0].title,
                content: results[0].content,
                time: results[0].time
            }
        })
    })
})

//获取留言列表接口
app.get('/api/getlist', (req, res) => {
    const sqlStr = 'SELECT * FROM leaving'
    db.query(sqlStr, (err, results) => {
        if (err) {
            console.error(err.message)
            return res.status(500).send({
                status: 500,
                message: '服务器内部错误'
            })
        }

        // 获取总留言数
        const totalCount = results.length;
        res.send({
            status: 200,
            message: '获取评论列表成功',
            data: results,
            totalCount: totalCount, // 返回总条数
            username: req.session.user // 返回当前登录的用户名
        })
    })
})

//新增留言接口
app.post('/api/addlist', (req, res) => {
    // 获取当前时间
    let time = new Date().toLocaleString()
    const sqlStr = 'INSERT INTO leaving (username, content, time) VALUES (?,?,?)'
    const params = [req.body.username, req.body.content, time]
    db.query(sqlStr, params, (err, results) => {
        if (err) {
            console.error(err.message)
            return res.status(500).send({
                status: 500,
                message: '服务器内部错误'
            })
        }

        res.send({
            status: 200,
            message: '新增评论成功',
            data: {
                id: results.insertId,  // 返回新留言的ID
                username: req.session.user,
                content: req.body.content,
                time: time
            }
        })
    })
})

// 删除留言接口
app.post('/api/deleteList', (req, res) => {
    const sqlStr = 'DELETE FROM leaving WHERE id = ?'
    const params = [req.body.id]
    db.query(sqlStr, params, (err, results) => {
        if (err) {
            console.error(err.message)
            return res.status(500).send({
                status: 500,
                message: '服务器内部错误'
            })
        }

        if (results.affectedRows === 0) {
            return res.status(404).send({
                status: 404,
                message: '未找到该评论'
            })
        }

        res.send({
            status: 200,
            message: '删除评论成功'
        })
    })
})

// 留言分页查询接口
app.post('/api/limitList', (req, res) => {
    const start = parseInt(req.body.num, 10) || 0; // 默认从第0条开始
    const count = 10; // 每页显示10条

    const sqlStr = 'SELECT * FROM leaving LIMIT ?, ?';
    const params = [start, count];

    db.query(sqlStr, params, (err, results) => {
        if (err) {
            return res.status(500).send({
                status: 500,
                message: '服务器内部错误'
            });
        }
        res.send({
            status: 200,
            data: results
        });
    });
});

// 分页查询接口
app.post('/api/limit', (req, res) => {
    const start = parseInt(req.body.num, 10) || 0; // 默认从第0条开始
    const count = 10; // 每页显示10条

    const sqlStr = 'SELECT * FROM article LIMIT ?, ?';
    const params = [start, count];

    db.query(sqlStr, params, (err, results) => {
        if (err) {
            return res.status(500).send({
                status: 500,
                message: '服务器内部错误'
            }<
上一篇:【AIGC进阶-ChatGPT提示词副业解析】阴谋论


下一篇:【汇编语言】内中断(三) —— 中断探险:从do0到特殊响应的奇妙旅程-4. 响应中断的特殊情况