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

项目简介:

这个项目是一个基于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: '服务器内部错误'
            });
        }
        res.send({
   
   
            status: 200,
            data: results
        });
    });
});

// 获取文章总数接口
app.get('/api/getArticleCount', (req, res) => {
   
   
    const sqlStr = 'SELECT COUNT(*) AS count FROM article';
    db.query(sqlStr, (err, results) => {
   
   
        if (err) {
   
   
            return res.status(500).send({
   
   
                status: 500,
                ge: '服务器内部错误'
            });
        }
        res.send({
   
   
            status: 200,
            totalCount: results[0].count
        });
    });
});




app.listen(80, () => {
   
   
    console.log('server is running at http://localhost:80')
})

六. 创建前端页面

登录页 login.html

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录页</title>
    <script src="../public/jQuery.js"></script>
    <script src="../public/axios.js"></script>

    <style>
        * {
   
   
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        html,
        body {
   
   
            height: 100%;
            font-family: Arial, sans-serif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Blue刂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值