Yii2 RESTful 响应需统一包装必须继承 yii\rest\Serializer 并重写 serialize() 方法,因其硬编码 JsonSerializer 不处理 code/message/data 结构,且全局 serializer 配置无效,须在控制器或模块中显式指定子类。

Yii2 RESTful 响应格式为什么不能直接改 JsonSerializer?
因为 Yii2 的 JsonSerializer 是硬编码在 yii\rest\Serializer 里的默认实现,它不处理状态码、不包装 data 字段、也不支持统一错误结构——你改了它的行为,但控制器里调用 $this->serialize() 时还是走原始逻辑,除非你重写整个 Serializer 类。
真正可控的入口是配置 serializer 组件,或者继承 yii\rest\Serializer 并覆盖 serialize() 方法。
- 别试图 patch
JsonSerializer::serialize():它只负责把对象转成数组,不决定顶层结构 - 别在 controller 里手动
Json::encode():会绕过ContentNegotiator和状态码设置 - 所有自定义必须通过
serializer配置或子类化yii\rest\Serializer实现
怎么让每个 API 响应都带 code、message、data 包裹?
核心是继承 yii\rest\Serializer,重写 serialize() 方法,在返回前做一层包装。注意:这个方法接收的是控制器返回的原始数据(可能是数组、ActiveRecord、Model 或 null),不是 JSON 字符串。
class ApiResponseSerializer extends \yii\rest\Serializer
{
public function serialize($data)
{
$result = parent::serialize($data);
$code = $this->getHttpCode();
$message = $this->getHttpMessage($code);
return [
'code' => $code,
'message' => $message,
'data' => $result,
'timestamp' => time(),
];
}
protected function getHttpCode()
{
return \Yii::$app->getResponse()->getStatusCode();
}
protected function getHttpMessage($code)
{
return \yii\helpers\ArrayHelper::getValue(
\yii\web\Response::$httpStatuses,
$code,
'Unknown Error'
);
}
}
- 必须保留
parent::serialize($data)调用:它负责处理Collection分页、Model属性过滤等逻辑 - 不要在
serialize()里调用Json::encode():序列化器只管产出数组,JSON 编码由ResponseFormatter完成 - 如果需要区分成功/失败,建议在 controller 中统一 throw
HttpException,靠errorHandler拦截并复用同一套序列化逻辑
如何兼容分页响应(DataProvider)和单模型响应?
yii\rest\Serializer 对 DataProvider 和普通数组/对象做了不同处理:前者会加 _meta 和 _links,后者直接展开。你的包装逻辑必须识别这层差异,否则分页字段会被塞进 data.data 里。
- 检查
$result是否含_meta键:有则说明是DataProvider输出,保持原结构再包裹 - 避免对
null做['data' => null]包装:有些接口 DELETE 成功后返回空体,应输出['code'=>200,'message'=>'OK','data'=>null],而非['data'=>[]] - 若需隐藏
_meta,可在控制器中设$this->serializer->collectionEnvelope = false,但更推荐前端适配标准分页字段
为什么设置了 serializer 却没生效?常见配置坑
最常见的失效原因是配置位置错了:必须在 controller 级别或模块级别设置 serializer,全局组件配置('components' => ['serializer' => [...]])不会自动被 REST 控制器读取。
- 正确方式是在 controller 类里加:
public $serializer = ['class' => 'app\components\ApiResponseSerializer']; - 或者在模块
behaviors()中为所有 REST 控制器统一设置:'serializer' => ['class' => 'app\components\ApiResponseSerializer'] - 如果用了
yii\rest\ActiveController,记得检查是否被父类的serializer属性覆盖(它默认是yii\rest\Serializer) - 调试时可临时在
serialize()开头加var_dump(get_class($this)); die;确认实例类型
最易忽略的一点:Yii2 的 serializer 不影响 OPTIONS 或预检请求的响应格式——那些走的是 CorsFilter,跟序列化器无关。









