Yii2 REST接口验证失败应返回422而非500,需正确配置JsonParser、使用bodyParams加载数据,并在afterAction中统一错误结构为{"code":422,"message":"Validation failed","errors":[...]}。

表单验证失败时返回 422 而不是 500
Yii2 REST 接口在模型验证失败时默认返回 422 Unprocessable Entity,这是符合 REST 规范的正确做法。但前提是:必须用标准方式触发验证,且请求体已被正确解析。
常见踩坑点:
- 前端没带
Content-Type: application/json请求头 → Yii 不调用 JsonParser → 验证逻辑收不到数据 → 报 500 或空模型 → 最终可能抛出异常而非 422 - 控制器里手动 new Model() 后直接
$model->load($request->post())→post()对 JSON 无效 →load()返回 false,但没显式检查就 save() → 验证失败却无提示 - 用了
$model->load($request->bodyParams)却没配JsonParser→bodyParams为空 → load 失败
让验证错误结构统一为 {“code”:422, “message”:”xxx”, “errors”: […]}
默认的 422 响应是纯数组格式:{"name":["Name cannot be blank."]},不带 code/message 包裹。要改成业务友好的结构,不能靠改 view,得接管序列化流程。
推荐做法是在基控制器中重写 afterAction():
- 判断响应状态码是否为 422
- 取出原始响应数据(即验证错误数组)
- 包装成标准格式:
['code' => 422, 'message' => 'Validation failed', 'errors' => $originalData] - 重新赋值给
Yii::$app->response->data
注意:不要在 beforeAction 或 action 内部 echo/print_r/exit,否则会中断 Yii 的响应生命周期。
JSON 请求下如何正确做验证
关键不是“怎么写 validate()”,而是“怎么让数据进得来、验得准”。
- 必须确保
config/web.php中 request 组件已配置:'parsers' => ['application/json' => 'yii\web\JsonParser'] - 控制器动作中,用
$request->bodyParams取数据(不是post(),也不是getRawBody()+ 手动 decode) - 模型加载推荐写法:
$model->load($request->bodyParams, '')—— 第二个参数传空字符串,避免默认尝试从_前缀提取 - 验证后,
if (!$model->validate())就能拿到完整错误,不用等save()才触发
自定义验证失败的 HTTP 状态码和消息
某些业务场景需要把特定字段错误映射为不同状态码,比如登录失败返回 401,权限不足返回 403。Yii2 允许在模型规则中抛出特定异常:
- 在
rules()里用function ($attribute)自定义验证逻辑 - 内部可抛
throw new \yii\web\UnauthorizedHttpException('Invalid credentials') - 或
throw new \yii\web\ForbiddenHttpException('Insufficient permissions')
这些异常会被 ErrorHandler 捕获,并自动设对应 status code;只要 error handler 已配置为 JSON 输出,就能自然融入统一错误格式。


















