博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
YII2中controller中的behaviors中的behavior内部是如何被使用的?
阅读量:4886 次
发布时间:2019-06-11

本文共 4647 字,大约阅读时间需要 15 分钟。

1. behaviors方法的调用:

在祖先对象components中有一个ensureBehaviors方法,代码如下:

/**     * Makes sure that the behaviors declared in [[behaviors()]] are attached to this component.     */    public function ensureBehaviors()    {        if ($this->_behaviors === null) {            $this->_behaviors = [];            foreach ($this->behaviors() as $name => $behavior) {                $this->attachBehaviorInternal($name, $behavior);            }        }    }

主要工作是对所有配置的behavior,通过attachBehaviorInternal方法生成对象,并且将当前宿主对象附加到每个behavior实例上,在behavior实例上以$owner变量存储,并且呢将每个behavior上配置的事件进行绑定,合适的时候好触发;

也就是三件事:1. 实例化behavior;2.将behavior实例与宿主关联;3.绑定behavior上配置的事件;而behavior对宿主产生作用,最核心的也是通过绑定的事件来产生。

粗粗看了下,这里绑定的事件都来源于宿主,比如:

[Controller::EVENT_BEFORE_ACTION => 'beforeAction']
[Response::EVENT_BEFORE_SEND => 'beforeSend',] [   ActiveRecord::EVENT_BEFORE_UPDATE => 'evaluateAttributes',   ActiveRecord::EVENT_BEFORE_INSERT => 'evaluateAttributes' ] 等等。系统中不少地方用到。采用事件的方式达到延迟触发的效果。并且以钩子的形式将插入正常的执行流程。 ensureBehavior方法在components类中__get,__set,__isset,__unset,__call,canSetProperty,canGetProperty,hasMethod,hasEventHandlers,on,off,trigger, getBehavior,attachBehavior,attachBehaviors,detachBehavior,detachBehaviors都有调用。也就是说在这些方法或者组件及后代组件的动作中将行为注入,绑定。 这样就清楚了,在宿主上以上方法中behavior被实例化,绑定到宿主对象,并且对宿主上的一些事件在behavior内部进行监听,绑定相应的处理handler,方便宿主上事件发生时,被触发和执行。
2. 我在工作中遇到的问题是:
 
public function beforeAction($action)    {        $response = Yii::$app->response;        if (Yii::$app->request->isPost) {            $data = Yii::$app->request->post();        } else {            $data = Yii::$app->request->get();        }        if (isset($data['UserId']) && isset($data['TokenId'])) {            if (!($user = User::findOne(['id' => $data['UserId'], 'ws_api_token' => $data['TokenId']]))) {                $response->statusCode = 401;                $response->statusText = "用户未找到或未授权";                return false;            } else {                Yii::$app->user->setIdentity($user);            }        } else {            $response->statusCode = 401;            $response->statusText = "缺少必要参数UserId,TokenId";            return false;        }        if (!parent::beforeAction($action)) return false;    }
 

  将父级beforeaction放在后面执行了,而在controller中父级中trigger了EVENT_BEFORE_ACTION事件,而该事件触发了在contentNegotiator中定义的方法beforeFilter。出现的结果如下:

An Error occurred while handling another error:yii\base\InvalidArgumentException: Response content must not be an array. in /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/web/Response.php:1063Stack trace:#0 /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/web/Response.php(337): yii\web\Response->prepare()#1 /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/web/ErrorHandler.php(135): yii\web\Response->send()#2 /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/base/ErrorHandler.php(111): yii\web\ErrorHandler->renderException(Object(yii\base\InvalidArgumentException))#3 [internal function]: yii\base\ErrorHandler->handleException(Object(yii\base\InvalidArgumentException))#4 {main}Previous exception:yii\base\InvalidArgumentException: Response content must not be an array. in /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/web/Response.php:1063Stack trace:#0 /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/web/Response.php(337): yii\web\Response->prepare()#1 /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/base/Application.php(392): yii\web\Response->send()#2 /home/nginx/tianjian_vue/pro-api/web/index.php(13): yii\base\Application->run()#3 {main}

  错误是返回内容未格式化。也就是说:

'contentNegotiator' => [                'class' => ContentNegotiator::className(),                'formats' => [                    'application/json' => Response::FORMAT_JSON                ]            ]

  behavior里对response里设置的数据返回格式没起作用。原因是啥呢?

原因是response的格式化是在behavior-contentNegotiator中方法negotiateContentType中完成的。如下:

foreach ($this->formats as $type => $format) {            $response->format = $format;            $response->acceptMimeType = $type;            $response->acceptParams = [];            break;        }
所以呢,把父级的beforeaction放到后面执行的化,就没有先触发事件,也就是没有给response对象设置为返回值为json,返回的格式才是json格式,而默认是html格式的。 再梳理一遍: 在controller中配置了behavior-contentNegotiator,在controller的父级ensureBehaviors调用中,将behavior实例化,绑定到controller宿主对象,并且设置事件监听,在controller中主要监听了 beforeaction事件。而ensureBehaviors在多种场合会发生自动调用,以上已列举,此处不赘述;尤其是在魔术方法__call的调用中,因为在controller运行中会若干次调用方法。所以behaviors被运行后绑定是毫无问题的。 behavior被确认绑定后,后面在controller及action运行的时候,运行了beforeaction,但是调用父级的beforeaction放后面了,就会导致这之前如果运行结果为false,直接返回,此时response的格式化就是默认的html格式, 所以就会出现上面那样的返回结果了。所以系统的方法放后面执行,则前面必须手动指定format了,或者手动触发一次事件EVENT_BEFORE_ACTION。
 
 
 

 

转载于:https://www.cnblogs.com/jiangtian/p/10988455.html

你可能感兴趣的文章
Python的函数参数传递:传值?引用?
查看>>
etcd集群一键安装运行
查看>>
进程、线程和多线程
查看>>
读写Session
查看>>
jQuery 中get 和post 方法传值注意事项
查看>>
网栅格布局
查看>>
非EF分页
查看>>
问题记录-java图片验证码显示乱码
查看>>
一些SVG 图片向下兼容优雅降级技术
查看>>
VS2013 调试时出现“表达式计算器中发生内部错误”的问题解决办法
查看>>
[C++]让CPU使用率曲线呈现为正弦曲线(一)
查看>>
20155313 2016-2017-2 《Java程序设计》第六周学习总结
查看>>
还是畅通工程
查看>>
Codeforces 585D. Lizard Era: Beginning(meet in the middle)
查看>>
Python_UUID模块
查看>>
ModelSim仿真教程
查看>>
Android 启动之 Bootloader(uboot)
查看>>
[CentOS7] iconv编程转换
查看>>
EJB中常用的设计模式
查看>>
C# 把第一次出现的重复数字移动到最后
查看>>