Yii2 內建支援 AJAX 請求檢測、JSON 格式化回應、參數自動綁定。本文提供控制器動作與前端 jQuery/Fetch 完整範例,涵蓋參數驗證、錯誤碼規範、CSRF 處理、回應結構統一。
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use yii\web\BadRequestHttpException;
class SupermarketsController extends Controller
{
// 關閉該動作的 CSRF 驗證(API 介面通常用 Token 驗證)
public function beforeAction($action) {
if ($action->id === 'sample') {
$this->enableCsrfValidation = false;
}
return parent::beforeAction($action);
}
public function actionSample() {
$request = Yii::$app->request;
// 僅接受 AJAX POST
if (!$request->isAjax || !$request->isPost) {
throw new BadRequestHttpException('僅支援 AJAX POST 請求');
}
// 參數獲取與驗證
$searchName = $request->post('searchname', '');
$searchBy = $request->post('searchby', '');
if ($searchName === '') {
return ['code' => 400, 'message' => '搜尋關鍵字不可為空', 'data' => null];
}
// 業務邏輯(示範)
$results = $this->searchSupermarkets($searchName, $searchBy);
// 統一 JSON 回應結構
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return [
'code' => 200,
'message' => 'success',
'data' => [
'list' => $results,
'total' => count($results),
],
];
}
private function searchSupermarkets(string $keyword, string $type): array {
// 實際應查詢資料庫/ES/Redis
return [
['id' => 1, 'name' => '全聯福利中心 - ' . $keyword],
['id' => 2, 'name' => '家樂福 - ' . $keyword],
];
}
}
?>
<script>
$('#searchBtn').on('click', function() {
$.ajax({
url: '= Yii::$app->request->baseUrl ?>/supermarkets/sample',
type: 'POST',
dataType: 'json',
data: {
searchname: $('#searchname').val().trim(),
searchby: $('#searchby').val()
},
beforeSend: function() {
$('#loading').show();
},
success: function(res) {
if (res.code === 200) {
renderList(res.data.list);
} else {
alert(res.message);
}
},
error: function(xhr) {
alert('請求失敗:' + xhr.statusText);
},
complete: function() {
$('#loading').hide();
}
});
});
</script>
<script>
async function search() {
const btn = document.getElementById('searchBtn');
btn.disabled = true;
try {
const res = await fetch('= Yii::$app->request->baseUrl ?>/supermarkets/sample', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest' // 標識 AJAX
},
body: new URLSearchParams({
searchname: document.getElementById('searchname').value.trim(),
searchby: document.getElementById('searchby').value
})
});
const data = await res.json();
if (data.code === 200) {
renderList(data.data.list);
} else {
alert(data.message);
}
} catch (e) {
console.error(e);
alert('網路錯誤');
} finally {
btn.disabled = false;
}
}
</script>
| 面向 | 建議 |
|---|---|
| 請求檢測 | `$request->isAjax` + `$request->isPost` 雙重檢查 |
| 參數獲取 | `$request->post('key', 'default')` 安全取值,避免 Notice |
| 驗證失敗 | 回傳 400 碼 + 明確訊息,前端統一處理 |
| 回應格式 | 統一 `{code, message, data}` 結構,`Response::FORMAT_JSON` 自動設 Header |
| CSRF | API 介面關閉 CSRF,改用 JWT/ApiToken 驗證 |
| 錯誤處理 | 拋 `BadRequestHttpException`/`ServerErrorHttpException` 讓錯誤處理器統一格式化 |
| 測試 | Postman/ curl 驗證非 AJAX 請求正確返回 400 |
// config/main.php
'components' => [
'errorHandler' => [
'errorAction' => 'site/error',
],
],
// 控制器
public function actionError() {
$exception = Yii::$app->errorHandler->exception;
if ($exception) {
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return [
'code' => $exception->statusCode ?? 500,
'message' => $exception->getMessage(),
'data' => null,
];
}
}
更多 Yii2 RESTful API 開發、ActiveController、資源類、速率限制、驗證器複用、API 版本控制、Swagger/OpenAPI 文檔生成、單元測試與 Mock,請參考本站專題系列。