Cakephp 3 在父类的 beforeFilter 中重定向

在我们的 CakePHP 3 应用程序中,我们发现了不同的行为.我们确信它在 CakePHP 2 中运行良好,所以我想在新版本中发生了一些变化.

In our CakePHP 3 application we found a different behaviour. We're sure that it worked well in CakePHP 2, so I suppose something changed in new version.

当用户访问这个url:/b2controller/myMethod,这些方法运行:

When user visits this url: /b2controller/myMethod, these methods run:

AppController::beforeFilter()  
BController::beforeFilter()  
B2Controller::beforeFilter()  
B2Controller::myMethod()  
B2Controller::myMethod2()    

然后用户被重定向到这个 url /ccontroller/myMethod10/

then user is redirected to this url /ccontroller/myMethod10/

但我们需要这个:

当用户访问时/b2controller/myMethod$isOk 条件为 true,然后将用户重定向到 /ccontroller/myMethod10/,没有运行 BController::beforeFilter()B2Controller::beforeFilter()B2Controller::myMethod()BController::MyMethod2().

When user visits /b2controller/myMethod and $isOk condition is true, then redirect user to /ccontroller/myMethod10/, without running BController::beforeFilter(), B2Controller::beforeFilter(), B2Controller::myMethod() and BController::MyMethod2().

我们的最小代码是这样的:

Our minimal code is like this:

class AppController {
  function beforeFilter(Event $event) {
        // set $isOk variable
        if ($isOk == TRUE) {
           return $this->redirect('/ccontroller/myMethod10/');
        }
        $aa=1;
        $ab=2;
  }
}

class BController extends AppController {
  function beforeFilter(Event $event) {
    parent::beforeFilter($event);

    $a=1;
    $b=2;
  }

  function myOtherMethod() {
      myOtherMethod2();
  }

  function myOtherMethod2() {
      ...
      ...
  }
}

class B2Controller extends BController {
  function beforeFilter(Event $event) {
    parent::beforeFilter($event);

    $m1=1;
    $m2=2;
  }

  function myMethod() {
      myMethod2();
  }

  function myMethod2() {
      ...
      ...
  }
}

class CController extends AppController {
  function beforeFilter(Event $event) {
    parent::beforeFilter($event);
  }

  function myMethod10() {
      ...
      ...
      ...
  }
}

如何让用户从主类的 beforeFilter 重定向到另一个控制器操作?请注意,发生重定向.但是用户在调用 myMethod()myMethod2() 后被重定向.

How can I make user to redirect to another controller action, from the beforeFilter of main class ? Note that redirect occurs. But user is redirected after calling myMethod() and myMethod2().

另请注意,还有其他控制器,如 CController 使用 beforeFilter 重定向行为.

Also note that there is other controllers like CController that uses beforeFilter redirect behaviour.

推荐答案

这里有 3 种有效的方法:

Here are 3 methods that works:

覆盖AppControllerstartupProcess方法:

// In your AppController
public function startupProcess() {
    // Compute $isOk
    if ($isOk) {
        return $this->redirect('/c/myMethod10');
    }
    return parent::startupProcess();
}

这是一种简短且非常干净的方法,所以如果可以的话,我会选择这个方法.如果这不符合您的需求,请参见下文.

This is a short and quite clean method, so I would go for this one if you can. If this does not fit your needs, see below.

注意:如果你使用这个方法,你的组件在你计算$isOk时可能不会被初始化,因为初始化是由parent::startupProcess<完成的/代码>.

Note: If you use this method, your components may not be initialized when you compute $isOk since the initialization is done by parent::startupProcess.

一种简单但不是很干净的方法可能是从 AppController::beforeFilter 发送响应:

One easy but not really clean way may be to send the response from AppController::beforeFilter:

public function beforeFilter(CakeEventEvent $event) {
    // Compute $isOk
    if ($isOk) {
        $this->response = $this->redirect('/c/myMethod10');
        $this->response->send();
        die();
    }
}

方法 3 - 使用 Dispatcher Filters

更干净"的方法是使用 调度器过滤器:

src/Routing/Filter/RedirectFilter.php:

In src/Routing/Filter/RedirectFilter.php:

<?php

namespace AppRoutingFilter;

use CakeEventEvent;
use CakeRoutingDispatcherFilter;

class RedirectFilter extends DispatcherFilter {

    public function beforeDispatch(Event $event) {
        // Compute $isOk
        if ($isOk) {
            $response = $event->data['response'];
            // The code bellow mainly comes from the source of Controller.php
            $response->statusCode(302);
            $response->location(CakeRoutingRouter::url('/c/myMethod10', true));
            return $response;
        }
    }
}

config/bootstrap.php中:

In config/bootstrap.php:

DispatcherFactory::add('Redirect');

并且您可以在 AppController 中删除重定向.如果您能够从 DispatcherFilter 计算 $isOk,这可能是最干净的方法.

And you can remove the redirection in your AppController. This may be the cleanest way if you are able to compute $isOk from the DispatcherFilter.

请注意,如果您有 beforeRedirect 事件,则不会使用此方法触发这些事件.

Note that if you have beforeRedirect event, these will not be triggered with this method.

这是我之前的答案,如果您有多个类似 B 的控制器,则效果不佳.

This was my previous answer which does not work very well if you have multiple B-like controllers.

你需要返回$this->redirect()返回的Response对象.实现此目的的一种方法是执行以下操作:

You need to return the Response object returned by $this->redirect(). One way of achieving this is by doing the following:

class BController extends AppController {

    public function beforeFilter(CakeEventEvent $event) {
        $result = parent::beforeFilter($event);
        if ($result instanceof CakeNetworkResponse) {
            return $result;
        } 
        // Your stuff
    }

}

if 后面的代码只有在没有重定向时才会执行(parent::beforeFilter($event) 没有返回 Response> 对象).

The code bellow the if is executed only if there was no redirection (parent::beforeFilter($event) did not return a Response object).

注意:我不知道你如何计算 isOk,但是如果你调用 $this->redirect()<,请注意无限重定向循环/code> 调用 /ccontroller/mymethod10 时.

Note: I do not know how you compute isOk, but be careful of infinite redirection loop if you call $this->redirect() when calling /ccontroller/mymethod10.

相关文章