文章目录
- Express连接MySQL实现CRUD并与模板页面数据交互全指南
Express连接MySQL实现CRUD并与模板页面数据交互全指南
在Express应用中,连接MySQL数据库并实现增删改查(CRUD)操作,同时与模板页面进行数据交互,是构建动态Web应用的核心流程。本文将详细介绍从数据库连接、路由设计、CRUD实现到模板数据传递的完整方案,帮助开发者掌握前后端数据交互的关键技术。
一、环境准备与依赖安装
1. 核心依赖
express:Web框架mysql2:MySQL客户端(支持Promise和连接池,性能优于旧版mysql)ejs:模板引擎(用于数据渲染,也可替换为Pug等)body-parser:解析表单提交数据(Express 4.16+已内置,可直接使用)
2. 安装命令
npm install express mysql2 ejs
二、MySQL连接配置
使用连接池(Connection Pool)管理数据库连接,提升性能和并发处理能力:
const mysql = require('mysql2/promise');
// 数据库配置
const dbConfig = {
host: 'localhost', // 数据库地址
user: 'root', // 用户名
password: 'your_password', // 密码
database: 'test_db', // 数据库名
port: 3306, // 端口(默认3306)
waitForConnections: true,
connectionLimit: 10, // 最大连接数
queueLimit: 0
};
// 创建连接池
const pool = mysql.createPool(dbConfig);
// 测试连接
async function testConnection() {
try {
const connection = await pool.getConnection();
console.log('数据库连接成功');
connection.release(); // 释放连接回池
} catch (err) {
console.error('数据库连接失败:', err.message);
}
}
// 执行SQL查询的通用方法
async function query(sql, params = []) {
try {
const [results] = await pool.execute(sql, params);
return results;
} catch (err) {
console.error('SQL执行错误:', err.message);
throw err; // 抛出错误供上层处理
}
}
module.exports = {
query,
testConnection
};
三、Express应用初始化与路由设计
1. 应用入口文件(app.js)
配置模板引擎、静态资源和路由:
const express = require('express');
const path = require('path');
const { testConnection } = require('./db');
const userRoutes = require('./routes/users');
// 初始化Express
const app = express();
const port = 3000;
// 配置模板引擎(EJS)
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views')); // 模板文件目录
// 解析表单数据(POST请求)
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// 静态资源目录(CSS、JS、图片等)
app.use(express.static(path.join(__dirname, 'public')));
// 测试数据库连接
testConnection();
// 注册路由
app.use('/users', userRoutes); // 用户相关CRUD路由
app.get('/', (req, res) => {
res.redirect('/users'); // 根路径重定向到用户列表
});
// 启动服务器
app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});
2. 数据库表设计(示例)
创建users表存储用户数据:
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
age INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
四、CRUD操作实现与模板数据交互
以用户管理为例,实现完整的增删改查,并在模板中展示和交互数据。
1. 路由处理(routes/users.js)
const express = require('express');
const router = express.Router();
const db = require('../db');
// 1. 查:获取所有用户并渲染列表页
router.get('/', async (req, res) => {
try {
const users = await db.query('SELECT * FROM users ORDER BY created_at DESC');
// 传递数据到模板(users变量将在EJS中使用)
res.render('users/list', {
title: '用户列表',
users: users,
message: req.query.message // 用于显示操作结果提示
});
} catch (err) {
res.status(500).send('获取用户失败:' + err.message);
}
});
// 2. 查:获取单个用户详情
router.get('/:id', async (req, res) => {
try {
const [users] = await db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
if (users.length === 0) {
return res.status(404).send('用户不存在');
}
res.render('users/detail', {
title: '用户详情',
user: users[0]
});
} catch (err) {
res.status(500).send('获取用户详情失败:' + err.message);
}
});
// 3. 跳转到添加用户表单页
router.get('/add', (req, res) => {
res.render('users/form', {
title: '添加用户',
user: {}, // 空对象,保持表单字段一致性
action: '/users/add' // 表单提交地址
});
});
// 4. 增:提交添加用户表单
router.post('/add', async (req, res) => {
try {
const {name, email, age} = req.body;
// 插入数据(使用参数化查询防SQL注入)
await db.query(
'INSERT INTO users (name, email, age) VALUES (?, ?, ?)',
[name, email, age || null]
);
// 重定向到列表页并携带成功消息
res.redirect('/users?message=用户添加成功');
} catch (err) {
res.status(500).send('添加用户失败:' + err.message);
}
});
// 5. 跳转到编辑用户表单页
router.get('/edit/:id', async (req, res) => {
try {
const [users] = await db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
if (users.length === 0) {
return res.status(404).send('用户不存在');
}
res.render('users/form', {
title: '编辑用户',
user: users[0],
action: `/users/edit/${req.params.id}`
});
} catch (err) {
res.status(500).send('获取编辑数据失败:' + err.message);
}
});
// 6. 改:提交编辑用户表单
router.post('/edit/:id', async (req, res) => {
try {
const {name, email, age} = req.body;
await db.query(
'UPDATE users SET name = ?, email = ?, age = ? WHERE id = ?',
[name, email, age || null, req.params.id]
);
res.redirect('/users?message=用户更新成功');
} catch (err) {
res.status(500).send('更新用户失败:' + err.message);
}
});
// 7. 删:删除用户
router.get('/delete/:id', async (req, res) => {
try {
await db.query('DELETE FROM users WHERE id = ?', [req.params.id]);
res.redirect('/users?message=用户删除成功');
} catch (err) {
res.status(500).send('删除用户失败:' + err.message);
}
});
module.exports = router;
2. 模板页面实现(EJS)
(1)用户列表页(views/users/list.ejs)
展示用户列表并提供操作入口:
<% include ../partials/header %>
<div class="container">
<h1><%= title %></h1>
<!-- 操作结果提示 -->
<% if (message) { %>
<div class="alert-success"><%= message %></div>
<% } %>
<!-- 添加用户按钮 -->
<a href="/users/add" class="btn-add">添加新用户</a>
<!-- 用户列表表格 -->
<table class="user-table">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>邮箱</th>
<th>年龄</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<% if (users.length > 0) { %>
<% users.forEach(user => { %>
<tr>
<td><%= user.id %></td>
<td><%= user.name %></td>
<td><%= user.email %></td>
<td><%= user.age || '未填写' %></td>
<td><%= new Date(user.created_at).toLocaleString() %></td>
<td class="actions">
<a href="/users/<%= user.id %>" class="btn-view">查看</a>
<a href="/users/edit/<%= user.id %>" class="btn-edit">编辑</a>
<a href="/users/delete/<%= user.id %>" class="btn-delete"
onclick="return confirm('确定要删除吗?')">删除</a>
</td>
</tr>
<% }) %>
<% } else { %>
<tr>
<td colspan="6" class="no-data">暂无用户数据</td>
</tr>
<% } %>
</tbody>
</table>
</div>
<% include ../partials/footer %>
(2)用户表单页(views/users/form.ejs)
用于添加和编辑用户(复用同一表单):
<% include ../partials/header %>
<div class="container">
<h1><%= title %></h1>
<!-- 表单:提交到指定action(添加/编辑) -->
<form action="<%= action %>" method="POST" class="user-form">
<div class="form-group">
<label for="name">姓名:</label>
<input type="text" id="name" name="name"
value="<%= user.name || '' %>" required>
</div>
<div class="form-group">
<label for="email">邮箱:</label>
<input type="email" id="email" name="email"
value="<%= user.email || '' %>" required>
</div>
<div class="form-group">
<label for="age">年龄:</label>
<input type="number" id="age" name="age"
value="<%= user.age || '' %>" min="0">
</div>
<div class="form-actions">
<button type="submit" class="btn-save">保存</button>
<a href="/users" class="btn-cancel">取消</a>
</div>
</form>
</div>
<% include ../partials/footer %>
(3)用户详情页(views/users/detail.ejs)
展示单个用户的详细信息:
<% include ../partials/header %>
<div class="container">
<h1><%= title %></h1>
<div class="user-detail">
<p><strong>ID:</strong><%= user.id %></p>
<p><strong>姓名:</strong><%= user.name %></p>
<p><strong>邮箱:</strong><%= user.email %></p>
<p><strong>年龄:</strong><%= user.age || '未填写' %></p>
<p><strong>创建时间:</strong><%= new Date(user.created_at).toLocaleString() %></p>
</div>
<div class="detail-actions">
<a href="/users/edit/<%= user.id %>" class="btn-edit">编辑</a>
<a href="/users" class="btn-back">返回列表</a>
</div>
</div>
<% include ../partials/footer %>
(4)公共模板组件(示例:views/partials/header.ejs)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %> - 用户管理系统</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
五、数据交互核心逻辑解析
1. 后端向前端(模板)传递数据
通过res.render(template, data)方法将数据传递到模板:
// 传递用户列表和标题
res.render('users/list', {
title: '用户列表',
users: users,
message: '操作成功'
});
在EJS模板中通过<%= 变量名 %>使用数据:
<h1><%= title %></h1>
<% users.forEach(user => { %>
<p><%= user.name %></p>
<% }) %>
2. 前端(表单)向后端传递数据
- 表单提交:通过
method="POST"将数据发送到后端路由,后端通过req.body获取:<form action="/users/add" method="POST"> <input name="name" value="张三"> </form> - URL参数:通过
/users/:id传递路径参数,后端通过req.params.id获取。 - 查询字符串:通过
/users?message=success传递,后端通过req.query.message获取(常用于跳转时传递提示信息)。
3. CRUD操作与页面跳转流程
- 查询(Read):直接渲染模板并传递数据库查询结果。
- 添加/编辑(Create/Update):
- 先渲染表单页(空表单或预填数据)。
- 表单提交后,后端处理数据并执行数据库操作。
- 操作完成后通过
res.redirect跳转到列表页,并携带结果提示。
- 删除(Delete):点击删除按钮后,后端执行删除操作,再重定向到列表页。
六、注意事项
1. 防SQL注入
始终使用参数化查询(?占位符),避免直接拼接SQL字符串:
// 安全(参数化查询)
db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
// 危险(可能导致SQL注入)
db.query(`SELECT * FROM users WHERE id = ${req.params.id}`);
2. 错误处理
- 数据库操作必须用
try/catch捕获错误,避免应用崩溃。 - 对用户输入进行验证(如邮箱格式、年龄范围),可使用
joi等验证库。
3. 连接池管理
- 无需手动关闭连接,使用
pool.execute后连接会自动释放回池。 - 根据服务器性能调整
connectionLimit(建议10-50)。
4. 模板数据安全
- 使用
<%= 变量 %>会自动转义HTML,防止XSS攻击(如用户输入包含<script>标签)。 - 若需显示原始HTML(如富文本),使用
<%- 变量 %>,但需确保内容安全。
七、总结
Express连接MySQL并与模板交互的核心流程可概括为:
- 数据库层:通过连接池管理MySQL连接,提供通用查询方法。
- 路由层:处理HTTP请求,调用数据库方法执行CRUD操作,通过
res.render传递数据到模板。 - 视图层:使用EJS模板渲染数据,通过表单和链接与后端交互。
关键技术点包括:参数化查询防注入、连接池提升性能、res.render数据传递、表单提交与重定向。掌握这些技术,可构建安全、高效的动态Web应用,实现数据的完整生命周期管理。
1214

被折叠的 条评论
为什么被折叠?



