Yii2 ActiveController的REST接口新增/修改记录时无法接收输入数据
我开发的Yii 2应用可正常响应获取数据的REST请求(如GET请求),但在尝试创建、修改记录(即使用POST、PUT等HTTP动词)时完全失败,提交的参数被完全忽略。
问题复现过程
- 基于*Yii 2 Advanced Project Template(Yii2高级项目模板)*创建空白Yii 2项目,执行命令如下:
composer create-project --prefer-dist yiisoft/yii2-app-advanced yii-application php init php yii migrate
- 参照官方文档为已存在的User模型创建REST风格的UserController:
namespace app\controllers; use yii\rest\ActiveController; class UserController extends ActiveController { public $modelClass = 'app\models\User'; }
- 配置URL规则:
'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ ['class' => 'yii\rest\UrlRule', 'controller' => 'user'], ], ]
- 开启JSON输入解析:
'request' => [ 'parsers' => [ 'application/json' => 'yii\web\JsonParser', ] ]
异常表现
GET /users、GET /users/12这类读数据请求可被正确处理。- 首次执行如下curl命令提交创建用户请求时,提交的JSON参数
{"username": "example", "email": "user@example.com"}被完全忽略,创建出的用户username和email字段均为空值:
curl -i -H "Accept:application/json" -H "Content-Type:application/json" \ -XPOST "http://localhost/users" \ -d '{"username": "example", "email": "user@example.com"}'
- 第二次及后续执行上述curl命令时,抛出*Integrity constraint violation(完整性约束违反)*异常:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '' for key 'username'
The SQL being executed was: INSERT INTOuser(status,created_at,updated_at) VALUES (9, 1654291700, 1654291700)
- 换用URL编码格式提交数据创建记录,问题依然存在:

问题出在Yii2高级项目模板自带的common\models\User模型验证规则配置上。
Yii2的ActiveController处理POST/PUT等写请求时,会调用模型的load()方法将请求参数批量赋值给模型属性。Yii2块赋值机制有明确安全限制:仅当前模型场景下配置了对应验证规则、被标记为安全(safe)的属性,才允许被块赋值接收,未配置验证规则的属性会被直接过滤。
高级模板默认生成的User模型,仅为status字段配置了验证规则,username、email等字段无任何验证规则,不属于安全属性,因此请求提交的这类参数会被全部过滤,最终生成的INSERT语句仅包含有默认值、自动赋值的status、created_at、updated_at字段,提交的用户名、邮箱参数完全不会进入SQL执行逻辑。
给User模型补充对应字段的验证规则,把需要通过REST接口批量赋值的字段标记为安全即可,示例配置如下:
public function rules() { return [ [['username', 'email'], 'required'], [['username', 'email'], 'string', 'max' => 255], ['username', 'unique'], ['email', 'email'], ['status', 'default', 'value' => self::STATUS_INACTIVE], ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_INACTIVE, self::STATUS_DELETED]], ]; }
如果不需要做严格的格式校验,也可以直接给对应字段加safe标记,允许块赋值:
public function rules() { return [ [['username', 'email', 'password_hash'], 'safe'], ['status', 'default', 'value' => self::STATUS_INACTIVE], ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_INACTIVE, self::STATUS_DELETED]], ]; }
修改完成后重新发起POST请求,参数即可正常被接收,不会再出现空值和唯一键冲突问题。
内容的提问来源于stack exchange,提问作者trejder

