Yii2 Controller/Action浏览器下重复触发两次问题求助
排查步骤
先在目标Action开头增加请求日志,确认两次请求的具体信息,排除规则匹配、异常跳转等问题:
public function actionTest() { // 新增日志逻辑 $logPath = \Yii::getAlias('@console') . '/runtime/request_log_' . date('Ymd') . '.txt'; $logContent = implode(' | ', [ date('Y-m-d H:i:s'), \Yii::$app->request->getMethod(), \Yii::$app->request->getAbsoluteUrl(), \Yii::$app->request->getUserAgent(), json_encode(\Yii::$app->request->getHeaders()->toArray()) ]) . PHP_EOL; file_put_contents($logPath, $logContent, FILE_APPEND); // 原有业务逻辑 $path = \Yii::getAlias('@console') . '/runtime/'; $n = 'A__' . self::random_uppercase(2); file_put_contents($path . $n . '.txt', time()); return $this->render('test'); }
触发两次请求后查看日志,可根据请求特征匹配对应解决方案:
常见场景及解决方案
1. 空资源属性导致重复请求(最高发)
如果你的视图/布局文件中存在空的src/href属性,例如<img src="">、<link href="">、<script src="">,绝大多数浏览器会将空地址解析为当前页面URL,自动发起第二次GET请求。
解决方案:
- 检查所有布局、视图模板,删除无意义的空资源标签,或补全正确的资源地址
- 单独配置favicon.ico路由,避免浏览器自动请求favicon时被Yii2解析到业务Action,可在web目录下直接放入favicon.ico文件,或在nginx/apache配置中优先处理静态资源请求
2. 浏览器预加载/预渲染请求
新版Chrome、Opera默认开启页面预加载功能,Firefox开启开发者控制台时也可能触发预取逻辑,这类请求会携带专属请求头:Purpose: prefetch、X-Moz: prefetch。
解决方案:
在Controller的beforeAction钩子中拦截预请求,不执行业务逻辑直接返回:
public function beforeAction($action) { $headers = \Yii::$app->request->getHeaders(); if ( ($headers->has('Purpose') && $headers->get('Purpose') === 'prefetch') || ($headers->has('X-Moz') && $headers->get('X-Moz') === 'prefetch') ) { \Yii::$app->response->statusCode = 204; return false; } return parent::beforeAction($action); }
3. HTTP跳转导致重复请求
如果站点配置了HTTP强制跳HTTPS、不带www跳转带www的规则,第一次请求会返回301/302状态码,浏览器自动发起第二次跳转后的请求。
解决方案:
- 直接使用最终跳转后的地址(如HTTPS地址)测试
- 优化web服务器跳转规则,避免多余的中间跳转
4. 幂等性兜底方案
如果排查后仍偶发重复请求,可针对业务逻辑加短期锁,保障10秒内同一用户同一Action仅执行一次:
public function actionTest() { $lockKey = 'action_idempotent_' . session_id() . '_' . \Yii::$app->request->getPathInfo(); // 已持有锁直接返回视图 if (\Yii::$app->cache->get($lockKey)) { return $this->render('test'); } // 加10秒锁 \Yii::$app->cache->set($lockKey, 1, 10); // 原有业务逻辑 $path = \Yii::getAlias('@console') . '/runtime/'; $n = 'A__' . self::random_uppercase(2); file_put_contents($path . $n . '.txt', time()); return $this->render('test'); }
内容的提问来源于stack exchange,提问作者Jon C.
相关产品推荐
相关产品推荐

