zoukankan      html  css  js  c++  java
  • YII2中controller中的behaviors中的behavior内部是如何被使用的?

    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:
    yiiaseInvalidArgumentException: Response content must not be an array. in /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/web/Response.php:1063
    Stack trace:
    #0 /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/web/Response.php(337): yiiwebResponse->prepare()
    #1 /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/web/ErrorHandler.php(135): yiiwebResponse->send()
    #2 /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/base/ErrorHandler.php(111): yiiwebErrorHandler->renderException(Object(yiiaseInvalidArgumentException))
    #3 [internal function]: yiiaseErrorHandler->handleException(Object(yiiaseInvalidArgumentException))
    #4 {main}
    Previous exception:
    yiiaseInvalidArgumentException: Response content must not be an array. in /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/web/Response.php:1063
    Stack trace:
    #0 /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/web/Response.php(337): yiiwebResponse->prepare()
    #1 /home/nginx/tianjian_vue/pro-api/vendor/yiisoft/yii2/base/Application.php(392): yiiwebResponse->send()
    #2 /home/nginx/tianjian_vue/pro-api/web/index.php(13): yiiaseApplication->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。




     
     
  • 相关阅读:
    (网页)中的简单的遮罩层
    (后端)shiro:Wildcard string cannot be null or empty. Make sure permission strings are properly formatted.
    (网页)jQuery的时间datetime控件在AngularJs中使用实例
    Maven Myeclipse 搭建项目
    MyBatis 环境搭建 (一)
    java 常用方法
    XML 基础
    JS BOM
    js 事件
    js 的使用原则
  • 原文地址:https://www.cnblogs.com/jiangtian/p/10988455.html
Copyright © 2011-2022 走看看