Skip to content

AJAX 数据库

AJAX(异步 JavaScript 和 XML)允许网页在后台与服务器通信,而无需进行完整的页面重新加载。这使得动态和交互式用户体验成为可能,例如即时获取数据库信息。

现代 AJAX 通常使用 JavaScript 的 fetch API,并且常用 JSON 代替 XML 进行数据交换,尽管核心概念保持不变。

本示例演示了如何通过从下拉列表中选择一个用户来触发 AJAX 请求到 PHP 脚本,然后该脚本查询 MySQL 数据库并将用户的详情返回到页面上显示。

概念性用户界面:


**Person info will be listed here...**

假设我们的数据库中有一个 users 表,其结构和数据如下:

CREATE TABLE users (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
FirstName VARCHAR(50) NOT NULL,
LastName VARCHAR(50) NOT NULL,
Age INT,
Hometown VARCHAR(50),
Job VARCHAR(50),
INDEX(LastName) -- 示例索引
);
INSERT INTO users (FirstName, LastName, Age, Hometown, Job) VALUES
('Peter', 'Griffin', 41, 'Quahog', 'Brewery'),
('Lois', 'Griffin', 40, 'Newport', 'Piano Teacher'),
('Joseph', 'Swanson', 39, 'Quahog', 'Police Officer'),
('Glenn', 'Quagmire', 41, 'Quahog', 'Pilot');

表数据:

ID名姓年龄家乡职业
1PeterGriffin41QuahogBrewery
2LoisGriffin40NewportPiano Teacher
3JosephSwanson39QuahogPolice Officer
4GlennQuagmire41QuahogPilot

示例讲解:HTML 和 JavaScript (客户端)

Section titled “示例讲解:HTML 和 JavaScript (客户端)”

HTML 设置了下拉列表。使用 fetch API 的 JavaScript 监听下拉列表的 change 事件,将选定的用户 ID 发送到服务器,并用服务器响应更新 txtHint div。

文件:index.html(或 .php)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AJAX Database Example</title>
<style>
#txtHint table { width: 100%; border-collapse: collapse; }
#txtHint table, #txtHint th, #txtHint td { border: 1px solid black; padding: 5px; text-align: left; }
</style>
</head>
<body>
<h1>Select User</h1>
<form>
<label for="user-select">Select a person:</label>
<select name="users" id="user-select">
<option value="">Select a person:</option>
<option value="1">Peter Griffin</option>
<option value="2">Lois Griffin</option>
<option value="3">Joseph Swanson</option>
<option value="4">Glenn Quagmire</option>
</select>
</form>
<br>
<div id="txtHint">**Person info will be listed here...**</div>
<script>
// 等待 DOM 完全加载
document.addEventListener('DOMContentLoaded', function() {
const userSelect = document.getElementById('user-select');
const hintContainer = document.getElementById('txtHint');
userSelect.addEventListener('change', function() {
const userId = this.value;
hintContainer.innerHTML = '**Loading...**'; // 提供反馈
if (userId === "") {
hintContainer.innerHTML = "**Person info will be listed here...";
return;
}
// 使用 Fetch API 向 PHP 后端发送请求
fetch(`get_user.php?q=${encodeURIComponent(userId)}`) // 使用 encodeURIComponent 以确保安全
.then(response => {
if (!response.ok) {
// 处理 HTTP 错误(例如 404, 500)
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.text(); // 将响应主体获取为文本(本例中为 HTML)
})
.then(html => {
// 使用从服务器接收到的 HTML 更新 hint 容器
hintContainer.innerHTML = html;
})
.catch(error => {
// 处理 fetch 错误(网络问题等)或服务器错误
console.error('Fetch Error:', error);
hintContainer.innerHTML = `**加载数据出错: ${error.message}**`;
});
});
});
</script>
</body>
</html>

JavaScript 讲解:

  • 给 <select> 元素的 change 事件附加了一个事件监听器。
  • 当用户选择一个选项时,会获取选定的 userId。
  • 如果 userId 为空,则重置提示区域。
  • fetch() 函数向 get_user.php 发送一个异步 GET 请求。
  • userId 作为 URL 参数 q 传递。encodeURIComponent() 确保值安全地添加到 URL 中。
  • .then(response => ...) 处理服务器的响应。它检查响应状态是否为 OK(例如 200)。
  • .then(html => ...) 处理响应主体(预期是 HTML 文本),并更新 txtHint div 的 innerHTML。
  • .catch(error => ...) 处理 fetch 过程中的任何错误(例如网络错误、服务器错误)。
  • 提供“Loading…”反馈可以改善用户体验。

此 PHP 脚本接收用户 ID(q),连接到数据库,使用预处理语句****安全地查询数据库,将结果格式化为 HTML 表格,然后发送回浏览器。

**安全警告:**处理用户输入进行数据库查询时,务必始终使用预处理语句,以防止 SQL 注入漏洞。

<?php
declare(strict_types=1);
// --- 数据库配置 ---
// !! 重要:安全地存储凭据,不要直接放在代码中。
// 考虑使用环境变量或放在网站根目录外的配置文件。
$dbHost = 'localhost';
$dbUser = 'your_db_username'; // 替换为你的数据库用户名
$dbPass = 'your_db_password'; // 替换为你的数据库密码
$dbName = 'your_database_name'; // 替换为你的数据库名
// --- 从请求获取用户 ID ---
// 使用 filter_input 更安全地访问 GET/POST 变量
$userId = filter_input(INPUT_GET, 'q', FILTER_VALIDATE_INT);
if ($userId === false || $userId === null) {
// 处理无效或缺失的 ID - 优雅地退出或返回错误消息
// 设置 HTTP 响应码有助于客户端脚本
// http_response_code(400); // 错误请求
echo "Invalid user ID provided.";
exit;
}
// --- 数据库连接(以 MySQLi 过程式风格为例)---
$conn = mysqli_connect($dbHost, $dbUser, $dbPass, $dbName);
// 检查连接
if (!$conn) {
// 在内部记录详细错误,向用户显示通用消息
error_log("Database Connection Error: " . mysqli_connect_error());
// http_response_code(500); // 内部服务器错误
echo "Error: Could not connect to the database.";
exit;
}
// 设置字符集(推荐)
mysqli_set_charset($conn, 'utf8mb4');
// --- 准备 SQL 语句(安全!)---
$sql = "SELECT FirstName, LastName, Age, Hometown, Job FROM users WHERE id = ?";
$stmt = mysqli_prepare($conn, $sql);
if (!$stmt) {
error_log("SQL Prepare Error: " . mysqli_error($conn));
echo "Error preparing database query.";
mysqli_close($conn);
exit;
}
// --- 绑定参数并执行 ---
mysqli_stmt_bind_param($stmt, "i", $userId); // 'i' 指定变量类型为整数
mysqli_stmt_execute($stmt);
// --- 获取结果 ---
$result = mysqli_stmt_get_result($stmt);
// --- 构建 HTML 响应 ---
if ($result && mysqli_num_rows($result) > 0) {
echo "<table>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
<th>Hometown</th>
<th>Job</th>
</tr>";
while ($row = mysqli_fetch_assoc($result)) {
echo "<tr>";
// 使用 htmlspecialchars 防止在 HTML 中回显数据时的 XSS 攻击
echo "<td>" . htmlspecialchars($row['FirstName'] ?? '', ENT_QUOTES, 'UTF-8') . "</td>";
echo "<td>" . htmlspecialchars($row['LastName'] ?? '', ENT_QUOTES, 'UTF-8') . "</td>";
echo "<td>" . htmlspecialchars((string)($row['Age'] ?? ''), ENT_QUOTES, 'UTF-8') . "</td>";
echo "<td>" . htmlspecialchars($row['Hometown'] ?? '', ENT_QUOTES, 'UTF-8') . "</td>";
echo "<td>" . htmlspecialchars($row['Job'] ?? '', ENT_QUOTES, 'UTF-8') . "</td>";
echo "</tr>";
}
echo "</table>";
} else if ($result) {
echo "No user found with ID: " . htmlspecialchars((string)$userId, ENT_QUOTES, 'UTF-8');
} else {
error_log("SQL Execute Error: " . mysqli_stmt_error($stmt));
echo "Error retrieving user data.";
}
// --- 清理 ---
mysqli_stmt_close($stmt);
mysqli_close($conn);
?>

PHP 脚本讲解:

  1. 配置: 设置数据库凭据(应安全存储)。
  2. 输入验证: filter_input() 获取并将 q 参数验证为整数。
  3. 数据库连接: 使用 mysqli_connect() 连接到 MySQL。错误处理至关重要。
  4. 字符集: 将连接字符集设置为 utf8mb4(推荐)。
  5. 预处理语句: 使用 mysqli_prepare() 准备带有占位符(?)的 SQL 查询。
  6. 绑定参数: 使用 mysqli_stmt_bind_param() 将验证后的 $userId 绑定到占位符。这是防止 SQL 注入的关键步骤。
  7. 执行: 使用 mysqli_stmt_execute() 执行预处理语句。
  8. 获取结果: mysqli_stmt_get_result() 获取结果。
  9. 构建响应: 如果找到用户,则使用数据构建 HTML 表格。输出时使用 htmlspecialchars() 确保安全。
  10. 错误处理: 在连接、准备和执行阶段检查错误。
  11. 清理: 使用 mysqli_stmt_close() 和 mysqli_close() 关闭语句和连接。

本示例使用 MySQLi 过程式风格。您也可以使用 MySQLi 面向对象风格或 PDO(PHP Data Objects)实现相同功能,这两种方式也都强烈支持预处理语句。