目录
- 项目简介:
- 主要功能
- 技术栈
- 项目目标:
- 效果展示:
- 一. 创建项目并初始化
- 二. 项目初始化
- 三. 安装项目所需要的包
- 四. 创建需要的数据库
- 五. 编写app.js
- 六. 创建前端页面
项目简介:
这个项目是一个基于Node.js和Express框架构建的简易博客系统,用户可以在该系统中进行注册、登录、发布文章、查看文章、删除文章以及留言等操作。项目利用MySQL数据库存储用户和文章数据,并通过前端页面提供用户友好的交互界面。以下是项目的主要功能简介:
主要功能
用户注册与登录
- 用户可以通过提供用户名和密码进行注册,注册后凭借用户名和密码登录系统。 * 系统会对用户密码进行加密储存,提高安全性。
文章管理
- 登录用户可以发布新的文章,包括输入标题和内容。
- 用户可以查看所有已发布的文章列表,并可以通过文章ID查找具体文章的详细内容。
- 登录用户可以删除自己发布的文章。
留言板功能
- 用户可以在留言板上发布留言和查看其他用户的留言。
- 留言也可以被删除。
文章与留言分页
- 在文章列表和留言板上支持分页功能,每页显示固定数量的内容,用户可翻页查看更多内容。
用户会话管理
- 系统通过Session管理用户登录状态,确保用户的安全性与隐私。
前端页面
- 采用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: '服务器内部错误'
}<