前後端分離架構下,JSON 成為事實標準傳輸格式。本文提供一個最小可行範例:前端以 jQuery serialize() 序列化表單,$.ajax 發送 application/x-www-form-urlencoded 請求並期望 JSON 回應;後端 PHP 解析 $_POST、組裝關聯陣列、json_encode 輸出。並涵蓋錯誤碼設計、CSRF 防護、除錯技巧。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>AJAX JSON Demo</title>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
</head>
<body>
<div id="result" style="margin-bottom:1rem; padding:1rem; background:#f5f5f5;">等待提交...</div>
<form id="userForm">
<div><label>姓名:</label><input type="text" name="username" required></div>
<div><label>年齡:</label><input type="number" name="age" min="1" max="120" required></div>
<div><label>職業:</label><input type="text" name="job" required></div>
<button type="submit">送出</button>
</form>
<script>
$(function() {
$('#userForm').on('submit', function(e) {
e.preventDefault(); // 防止傳統提交
var $form = $(this);
var $btn = $form.find('button').prop('disabled', true);
$('#result').text('送出中...');
$.ajax({
url: 'api.php',
type: 'POST',
dataType: 'json', // 關鍵:告訴 jQuery 自動 parseJSON
data: $form.serialize(), // 依 name 屬性序列化為 key=value&key=value
success: function(res) {
if (res.code === 0) {
$('#result').html('成功:' + res.data.username + ' / ' + res.data.age + ' / ' + res.data.job);
} else {
$('#result').html('業務錯誤:' + res.msg);
}
},
error: function(xhr, status, err) {
$('#result').html('網路/解析錯誤:' + status + ' - ' + err + ' | 回應:' + xhr.responseText);
},
complete: function() {
$btn.prop('disabled', false);
}
});
});
});
</script>
</body>
</html>
<?php
header('Content-Type: application/json; charset=utf-8');
// 簡易 CSRF 驗證 (生產環境請用框架內建或雙重 Cookie 模式)
if (empty($_POST['_csrf']) || $_POST['_csrf'] !== $_SESSION['csrf_token']) {
echo json_encode(['code' => 403, 'msg' => 'CSRF token 無效']);
exit;
}
// 1. 接收與清洗
$username = trim($_POST['username'] ?? '');
$age = (int)($_POST['age'] ?? 0);
$job = trim($_POST['job'] ?? '');
// 2. 驗證
$errors = [];
if ($username === '') $errors[] = '姓名不可為空';
if ($age < 1 || $age > 120) $errors[] = '年齡需 1-120';
if ($job === '') $errors[] = '職業不可為空';
if ($errors) {
echo json_encode(['code' => 400, 'msg' => implode(';', $errors)]);
exit;
}
// 3. 業務處理 (此處僅回傳)
// $id = User::create(['username'=>$username, 'age'=>$age, 'job'=>$job]);
// 4. 統一回傳格式
echo json_encode([
'code' => 0,
'msg' => 'ok',
'data' => [
'username' => $username,
'age' => $age,
'job' => $job,
],
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
var data = {
username: $('input[name="username"]').val(),
age: $('input[name="age"]').val(),
job: $('input[name="job"]').val(),
_csrf: $('input[name="_csrf"]').val()
};
$.post('api.php', data, function(res) {
// res 已自動解析為物件 (dataType: json)
if (res.code === 0) { /* ... */ }
}, 'json');
| 欄位 | 型別 | 說明 |
|---|---|---|
| code | int | 0=成功;400=參數錯誤;401=未登入;403=權限不足;500=伺服器錯誤 |
| msg | string | 人類可讀訊息,前端直接顯示 |
| data | mixed | 成功時的業務資料 (物件/陣列/純值);失敗時可為 null 或錯誤詳情 |
header() 前無任何輸出,錯誤改走 error_log()。json_encode 加 JSON_UNESCAPED_UNICODE;資料庫連線 SET NAMES utf8mb4。<input type="hidden" name="_csrf" value="...">。serialize() 不支援 File 物件 → 改用 FormData (見「非同步上傳」篇)。JSend 或 RFC 7807 Problem Details 標準化錯誤回應。$.ajaxSetup({ dataFilter: ... }) 自動處理 code ≠ 0 的彈窗/導向登入頁。vlucas/phpdotenv 管理環境變數、respect/validation 做聲明式驗證。💡 核心心法:AJAX + JSON 的關鍵在於「約定大於配置」。前後端共同維護一份 API 契約文件 (OpenAPI/Swagger),定義請求參數、回應結構、錯誤碼,才能在團隊協作中實現真正的平行開發。