yii2控制器返回json的核心是让框架自动序列化数组并设置正确响应头,而非手动json_encode()或echo;需先设yii::$app->response->format = response::format_json(推荐beforeaction中统一配置),再直接return数组,验证失败时须显式调用$model->validate()并检查geterrors(),同时注意禁用csrf验证及使用422状态码。

Yii2控制器返回JSON数据,核心不是“怎么转成JSON”,而是让框架自动序列化数组并设置正确的响应头。手动json_encode()或echo会绕过Yii的响应生命周期,容易出错、丢失状态码、破坏中间件行为。
控制器动作里直接 return 数组
只要控制器继承yii\web\Controller,且已设置Yii::$app->response->format = Response::FORMAT_JSON(推荐在beforeAction()中统一设),return一个普通PHP数组就会被自动转为JSON响应:
- 不需要调用
Yii::$app->response->send()或exit - 避免
echo json_encode([...])——它跳过格式控制,Content-Type可能仍是text/html - 若用
$this->asJson([...]),它内部已设好format和headers,更稳妥
验证失败时必须显式调用 validate()
常见错误是只写$model->load() && $model->save(),但save()失败时默认返回false,不抛异常,也**不会填充getErrors()**(除非你传$runValidation = true)。正确流程是:
-
$model->load(Yii::$app->request->post())加载数据 - 必须紧接着调用
$model->validate()触发规则检查 - 再用
$model->getErrors()取错误,否则返回空数组 - 不要依赖
save()的返回值来判断字段级错误
CSRF 验证导致 load() 静默失败
纯AJAX表单(如小程序、Vue前端)常不带CSRF token,而$model->load()在CSRF验证失败时**直接返回false,且不报错**,导致你以为数据没提交成功,实际是token校验拦住了。解决方式:
- 前端确保请求头含
X-CSRF-Token(从Yii::$app->request->csrfToken获取) - 或在控制器类中临时禁用:
public $enableCsrfValidation = false; - 不建议全局关,仅对无浏览器上下文的API action关闭
HTTP状态码别忽略 422
验证失败语义上属于“无法处理的实体”,用422比200+success:false更规范。前端fetch可直接用response.ok判断,不用解析body:
- 设置状态码:
Yii::$app->response->setStatusCode(422) - 放在return之前,否则无效
- 如果用
$this->asJson(),需额外调用setStatusCode(),它本身不改状态码 - 400也可用,但422是REST API事实标准
最易被忽略的是:验证逻辑和响应格式必须解耦。有人把if (!$model->validate()) { echo '...'; exit; }塞进action里,这会中断Yii的事件链,后续日志、缓存、CSP头都可能失效。真正的“返回JSON”是让整个响应管道走通,而不是中途截胡。











