Yii2验证码验证异常求助:validate通过但save时验证失败
这个问题我之前也碰到过,核心原因是Yii2的验证码验证器在验证通过后会自动刷新session中的验证码值,而$model->save()默认会再次执行validate(),这时候验证码已经不是用户输入的那个了,自然验证失败。结合你的代码,我给你几个可行的解决方案:
首先修正一个代码遗漏点
你的UserController的actionCreate里,接收POST数据后没有调用$model->load(Yii::$app->request->post()),这会导致模型无法获取用户输入的验证码(以及其他表单字段),这大概率是你漏写的,必须先补上,否则前面的validate()根本无法正确执行。
解决方案一:直接跳过save时的验证
既然你已经在前面手动执行了validate()并且通过了,那可以直接调用$model->save(false),这样save方法会跳过验证步骤,直接保存数据:
修改UserController的actionCreate代码:
public function actionCreate() { $model = new Users(); $userLoginExist = Yii::$app->user->identity; if(!$userLoginExist) { if (Yii::$app->request->isPost) { // 先加载POST数据到模型 if ($model->load(Yii::$app->request->post()) && $model->validate()) { // 使用save(false)跳过验证 if ($model->save(false)) { // 这里添加保存成功后的逻辑,比如跳转页面 // return $this->redirect(['view', 'id' => $model->id]); } } } // 记得返回渲染创建页面的逻辑,你代码里漏了 return $this->render('create', ['model' => $model]); } else { // 用户已登录的处理,比如跳转首页 return $this->redirect(['index']); } }
解决方案二:使用场景控制验证码验证(更规范)
这种方法通过模型场景来控制验证码只在表单提交的验证阶段生效,save时不验证该字段,更符合Yii2的设计规范:
1. 修改Users模型的验证规则
给验证码验证器指定on属性,只在create场景下生效:
class Users extends \yii\db\ActiveRecord { public static function tableName() { return 'users'; } public function rules() { return [ // 其他字段的验证规则... ["captcha", 'captcha', 'on' => 'create'] ]; } }
2. 修改UserController的actionCreate代码
初始化模型时指定create场景,验证通过后切换到默认场景(或自定义的save场景),再执行save:
public function actionCreate() { // 初始化模型时指定create场景 $model = new Users(['scenario' => 'create']); $userLoginExist = Yii::$app->user->identity; if(!$userLoginExist) { if (Yii::$app->request->isPost) { if ($model->load(Yii::$app->request->post()) && $model->validate()) { // 切换到默认场景,此时验证码规则不生效 $model->scenario = 'default'; if ($model->save()) { // 保存成功后的逻辑 // return $this->redirect(['view', 'id' => $model->id]); } } } return $this->render('create', ['model' => $model]); } else { return $this->redirect(['index']); } }
原理补充
Yii2的CaptchaValidator在验证通过时,会调用CaptchaAction::validate()方法,这个方法内部会在验证成功后立即生成新的验证码并更新到session中,目的是防止验证码被重复使用。所以当你后续调用$model->save()(默认会执行validate)时,session里的验证码已经被刷新,自然和用户输入的不符,导致验证失败。
内容的提问来源于stack exchange,提问作者user7961310

