yii2 response多次输出问题的查找
{ "IsSuccess": 1, "ErrMsg": "OK", "Data": { "IsSuccess": 1, "ErrMsg": "OK", "Data": { "IsSuccess": 1, "ErrMsg": "OK", "Data": { "IsSuccess": 1, "ErrMsg": "OK", "Data": { "IsSuccess": 1, "ErrMsg": "OK", "Data": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "error_code": 1, "message": "OK", "res_msg": { "available": [ "/api/auth/view", ], "assigned": [ "/*", "/admin/*", "/admin/api/*" ] } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } }
以上是现象。
action:
public function actionRefresh() { $model = new Route(); $model->invalidate(); return $model->getRoutes(); }
访问这个action产生的。因为我这个controller继承了同事写的一个基controller,代码如下:
public function init() { parent::init(); //绑定beforeSend事件,更改数据输出格式 Yii::$app->getResponse()->on(Response::EVENT_BEFORE_SEND, [$this, 'beforeSend']); } /** * 更改数据输出格式 * 默认情况下输出Json数据 * @param \yii\base\Event $event */ public function beforeSend($event) { /* @var $response \yii\web\Response */ $response = $event->sender; $msg = $response->statusText; $statusCode = $response->statusCode; $isSuccess = $response->getIsSuccessful(); if(isset($response->data['code']) && $response->data['code']==0){ $code = 0; }else{ if($isSuccess){ $code = 1; }else{ $code = 0; } } if ($response->statusCode>=400) { //异常处理 if (true && $exception = Yii::$app->getErrorHandler()->exception) { $data = $response->data; //$data = $this->convertExceptionToArray($exception); } //Model出错了 if ($response->statusCode==422) { $messages=[]; foreach ($response->data as $v) { $messages[] = $v['message']; } //请求错误时数据为 {"success":false,"data":{"name":"Not Found","message":"页面未找到。","code":0,"status":404}} $data = [ 'error_code' =>$code, 'message'=> implode(" ", $messages), 'res_msg'=>$response->data ]; } $response->isSent = true; // $response->statusCode = 200; } elseif ($response->statusCode>=300) { // $response->statusCode = 200; $data = $this->convertExceptionToArray(new ForbiddenHttpException(Yii::t('yii', 'Login Required'))); } else{ $data = $response->data; } //请求正确时数据 $response->data = [ 'error_code' =>$code, 'message' => $msg, 'res_msg' => empty($data) ? array('message'=>'暂无数据') : $data, ]; $response->format = Response::FORMAT_JSON; // \Yii::$app->getResponse()->getHeaders()->set('Access-Control-Allow-Origin', '*'); // \Yii::$app->getResponse()->getHeaders()->set('Access-Control-Allow-Credentials', 'true'); }
在beforeSend中Yii::info($response->statusCode);
发现该事件Response::EVENT_BEFORE_SEND被多次触发,这就导致了上面那个现象。
在基础controller中没有检测该事件是否绑定handler,造成了多次多次重复绑定,因为在该次执行中对多个controller进行了实例化。
init中改为:
public function init() { parent::init(); //绑定beforeSend事件,更改数据输出格式 if (!Yii::$app->response->hasEventHandlers(Response::EVENT_BEFORE_SEND)) { // 避免重复绑定 Yii::$app->getResponse()->on(Response::EVENT_BEFORE_SEND, [$this, 'beforeSend']); } }
方法
不过在加入了该判断后,发现少了一些输出,不过还有六次输出,如下:
{ "IsSuccess": 1, "ErrMsg": "OK", "Data": { "IsSuccess": 1, "ErrMsg": "OK", "Data": { "IsSuccess": 1, "ErrMsg": "OK", "Data": { "IsSuccess": 1, "ErrMsg": "OK", "Data": { "IsSuccess": 1, "ErrMsg": "OK", "Data": { "error_code": 1, "message": "OK", "res_msg": { "available": [ "/api/auth/view", "/api/auth/create" ], "assigned": [ "/*", "/admin/*", "/admin/api/*", ] } } } } } } }
突然发现前面输出的是:
"IsSuccess": 1,
"ErrMsg": "OK",
"Data":{}
而这是我在另外一个基controller中绑定的事件发挥的结果格式,如下:
public function init() { parent::init(); Event::on( Response::className(), Response::EVENT_BEFORE_SEND, [$this, 'formatDataBeforeSend'] ); }
很奇怪,他为啥跟这个混在一起了?
对了,这是因为出现这种情况是在获取系统所有route的方法getAppRoutes中,而其中有对所有controller进行实例化的动作。症结就在这里了。
将init方法改造为:
public function init() { parent::init(); if (!Event::hasHandlers(Response::className(), Response::EVENT_BEFORE_SEND)) { Event::on( Response::className(), Response::EVENT_BEFORE_SEND, [$this, 'formatDataBeforeSend'] ); } }
不过在这样处理后,还有问题,如下:
{ "IsSuccess": 1, "ErrMsg": "OK", "Data": { "error_code": 1, "message": "OK", "res_msg": { "available": [ "/api/auth/view", "/api/auth/create", "/api/auth/update", ], "assigned": [ "/*", "/admin/*" ] } } }
还有问题,不过这个问题的原因就在于这里因为对那个作为路由controller的中绑定事件的触发,加上正常的触发,导致了这个问题。
这个问题除了前面加上判断之外,还得处理一下,如下:
public function init() { parent::init(); if ( !Event::hasHandlers(Response::className(), Response::EVENT_BEFORE_SEND) && !Yii::$app->response->hasEventHandlers(Response::EVENT_BEFORE_SEND) ) { Event::on( Response::className(), Response::EVENT_BEFORE_SEND, [$this, 'formatDataBeforeSend'] ); } }
因为涉及到两种返回格式,且在一次访问中,多次触发controller实例化,这种情况一般是不常见的。所以出现这种现象是特定情况。问题已找到。