<!-- index.html -->
<form id="knowledgeForm">
<label>知識點: <input name="question" required></label><br><br>
<label>答案: <input name="answer" required></label><br><br>
<button type="submit">添加</button>
</form>
<div id="result"></div>
<script>
// 原生 XMLHttpRequest (兼容 IE6+)
document.getElementById('knowledgeForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const xhr = new XMLHttpRequest();
xhr.open('POST', 'insert.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); // 標識 AJAX 請求
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
document.getElementById('result').textContent = xhr.responseText;
document.getElementById('knowledgeForm').reset();
} else {
document.getElementById('result').textContent = '請求失敗: ' + xhr.status;
}
};
xhr.onerror = () => document.getElementById('result').textContent = '網絡錯誤';
xhr.send(formData);
});
</script>
<?php
// insert.php (PDO 預處理防注入)
header('Content-Type: text/plain; charset=utf-8');
require_once 'db.php'; // 返回 $pdo 實例
try {
$question = trim($_POST['question'] ?? '');
$answer = trim($_POST['answer'] ?? '');
if ($question === '' || $answer === '') throw new Exception('參數不能為空');
$stmt = $pdo->prepare('INSERT INTO knowledge (question, answer, created_at) VALUES (?, ?, NOW())');
$stmt->execute([$question, $answer]);
echo "輸入成功 (ID: {$pdo->lastInsertId()})";
} catch (Throwable $e) {
http_response_code(500);
error_log($e);
echo '服務器錯誤: ' . $e->getMessage();
}
async function submitKnowledge(formEl) {
const formData = new FormData(formEl);
try {
const resp = await fetch('insert.php', {
method: 'POST',
body: formData,
credentials: 'same-origin', // 攜帶 Cookie
headers: { 'X-Requested-With': 'fetch' }
});
const text = await resp.text();
if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${text}`);
alert(text); // 或顯示在頁面
formEl.reset();
} catch (err) {
console.error(err);
alert('提交失敗: ' + err.message);
}
}
document.getElementById('knowledgeForm').addEventListener('submit', e => {
e.preventDefault();
submitKnowledge(e.target);
});
<?php
// api_response.php
function api_success($data = null, string $msg = 'ok', int $code = 200): never {
http_response_code($code);
echo json_encode(['code' => 0, 'msg' => $msg, 'data' => $data], JSON_UNESCAPED_UNICODE);
exit;
}
function api_error(string $msg, int $httpCode = 400, int $bizCode = -1, $data = null): never {
http_response_code($httpCode);
echo json_encode(['code' => $bizCode, 'msg' => $msg, 'data' => $data], JSON_UNESCAPED_UNICODE);
exit;
}
// insert.php 使用
require 'api_response.php';
require 'db.php';
$question = trim($_POST['question'] ?? '');
$answer = trim($_POST['answer'] ?? '');
if (!$question || !$answer) api_error('知識點與答案均必填');
try {
$stmt = $pdo->prepare('INSERT INTO knowledge (question, answer) VALUES (?, ?)');
$stmt->execute([$question, $answer]);
api_success(['id' => $pdo->lastInsertId()], '創建成功');
} catch (PDOException $e) {
if ($e->getCode() === '23000') api_error('數據重複', 409, 1001);
api_error('數據庫錯誤', 500, 5001);
}
interface ApiResponse { code: number; msg: string; data: T; }
class ApiError extends Error { constructor(public code: number, msg: string, public data: any) { super(msg); } }
async function request(url: string, options: RequestInit = {}): Promise {
const resp = await fetch(url, {
...options,
headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'fetch', ...options.headers },
credentials: 'same-origin',
});
const json = (await resp.json()) as ApiResponse;
if (json.code !== 0) throw new ApiError(json.code, json.msg, json.data);
return json.data;
}
// 使用
interface Knowledge { id: number; question: string; answer: string; }
const data = await request('insert.php', {
method: 'POST',
body: JSON.stringify({ question: 'PHP 8.1 新特性', answer: 'Enum, Fiber, Never...' })
});
console.log('Created:', data.id);
X-CSRF-Token。PDO::prepare),拒絕字符串拼接。htmlspecialchars,API 返回 JSON 設置 Content-Type: application/json 並 X-Content-Type-Options: nosniff。202 Accepted,前端輪詢/WebSocket 獲取結果。從原生 XMLHttpRequest 到 Fetch API、統一響應封裝、TypeScript 類型安全,AJAX 交互已演進為**前後端契約化、工程化**的標準流程。核心不變:**前端收集數據 → 異步發送 → 服務端驗證入庫 → 結構化響應 → 前端更新 UI**。掌握預處理語句、錯誤碼設計、CSRF/速率限制等安全基線,才能寫出經得起生產考驗的代碼。