Laravel 5.8自定义Recaptcha验证规则重复调用问题求助
我之前也碰到过类似的情况,结合你的描述和Laravel 5.8的验证机制,大概率是以下几个原因导致验证规则被调用两次,进而触发Google Recaptcha的timeout-or-duplicate错误(毕竟Recaptcha token只能被校验一次):
1. 表单请求的prepareForValidation()触发二次验证
如果你的RegistrationRequest类重写了prepareForValidation()方法,并且在这个方法里修改了请求数据(比如合并参数、格式化字段),Laravel会自动重新触发验证流程,导致你的自定义Recaptcha规则被执行两次。
举个例子,假设你的请求类里有这段代码:
protected function prepareForValidation() { $this->merge([ 'email' => strtolower($this->input('email')), 'username' => trim($this->input('username')), ]); }
修改请求参数后,Laravel会重新运行整个验证逻辑,同一个Recaptcha token被提交给Google校验两次,第二次自然就返回timeout-or-duplicate了。
2. 重复的验证逻辑调用
检查你的路由和控制器代码,是否存在双重验证的情况:
- 路由被
ValidationMiddleware中间件包裹,同时控制器方法又注入了RegistrationRequest表单请求; - 控制器方法中手动调用了
$request->validate()或者$this->validate(),同时方法参数使用了表单请求类。
这两种情况都会触发两次验证流程,导致token被重复校验。
3. Laravel 5.8表单验证的内部机制问题
你提到的官方issue #25736确实涉及验证规则重复执行的场景,比如使用条件验证(如sometimes)或者规则依赖时,可能会触发重复验证。不过你的场景是required+自定义Rule,大概率还是前面两种情况,但也可以排查下自定义Rule中是否存在修改请求数据的逻辑,间接触发了二次验证。
快速解决方法
既然控制器静态方法校验可以正常工作,说明单次校验没问题。如果想继续使用表单请求的方式,可以在自定义Rule中加入缓存逻辑,避免重复请求Google接口:
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; public function passes($attribute, $value) { // 用token作为缓存键,避免重复校验同一个token $cacheKey = 'recaptcha_validation_' . $value; if (Cache::has($cacheKey)) { return Cache::get($cacheKey); } // 调用Google Recaptcha接口校验 $response = Http::post('https://www.google.com/recaptcha/api/siteverify', [ 'secret' => config('services.recaptcha.secret'), 'response' => $value, 'remoteip' => request()->ip(), ]); $validationResult = $response->json(); $isValid = $validationResult['success'] ?? false; // 缓存校验结果5分钟(根据Recaptcha token有效期调整) Cache::put($cacheKey, $isValid, 5); Log::info('Recaptcha校验日志', ['success' => $isValid]); return $isValid; }
这样即使验证规则被调用两次,第二次会直接读取缓存的结果,不会重复请求Google接口,也就不会出现timeout-or-duplicate错误。
另外,建议先排查RegistrationRequest的prepareForValidation()方法和路由/控制器的验证逻辑,找到触发二次验证的根源,从根本上解决问题。
内容的提问来源于stack exchange,提问作者Marshall

