首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

AJAX + PHP 實現無刷新數據入庫:從原生 JS 到現代 Fetch/API 設計模式

     編輯:PHP 綜合知識

經典原生實現 (教學用)

<!-- 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();
}

現代 Fetch API 寫法 (推薦新項目)

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);
});

進階:統一 API 響應格式 + 錯誤碼體系

<?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);
}

前端統一請求封裝 (TypeScript 示例)

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);

安全與最佳實踐清單

  1. CSRF 保護:雙重 Cookie 模式 (Laravel/Symfony 自動)、或 Header 驗證 X-CSRF-Token。
  2. 輸入驗證:服務端**必須**驗證長度、格式、類型,前端驅動僅為體驗。
  3. SQL 注入防護:全量使用**預處理語句** (PDO::prepare),拒絕字符串拼接。
  4. XSS 防護:輸出到 HTML 時 htmlspecialchars,API 返回 JSON 設置 Content-Type: application/json 並 X-Content-Type-Options: nosniff。
  5. 速率限制:基於 IP/用戶 的令牌桶/漏桶算法 (Redis 實現),防刷接口。
  6. 異步解耦 (高併發):寫入請求推入隊列 (Redis/RabbitMQ),Worker 異步入庫,立即返回 202 Accepted,前端輪詢/WebSocket 獲取結果。
  7. 日誌與追蹤:請求 ID (Trace-ID) 貫穿前後端,結構化日誌 (JSON) 接入 ELK/Loki。

總結

從原生 XMLHttpRequest 到 Fetch API、統一響應封裝、TypeScript 類型安全,AJAX 交互已演進為**前後端契約化、工程化**的標準流程。核心不變:**前端收集數據 → 異步發送 → 服務端驗證入庫 → 結構化響應 → 前端更新 UI**。掌握預處理語句、錯誤碼設計、CSRF/速率限制等安全基線,才能寫出經得起生產考驗的代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved