ZF2 Use Redirect in outside of controller

2019-03-31 01:05发布

问题:

I'm working on an ACL which is called in Module.php and attached to the bootstrap.

Obviously the ACL restricts access to certain areas of the site, which brings the need for redirects. However, when trying to use the controller plugin for redirects it doesn't work as the plugin appears to require a controller.

What's the best way to redirect outside from outside of a controller? The vanilla header() function is not suitable as I need to use defined routes.

Any help would be great!

Cheers-

回答1:

In general, you want to short-circuit the dispatch process by returning a response. During route or dispatch you can return a response to stop the usual code flow stop and directly finish the result. In case of an ACL check it is very likely you want to return that response early and redirect to the user's login page.

You either construct the response in the controller or you check the plugin's return value and redirect when it's a response. Notice the second method is like how the PRG plugin works.

An example of the first method:

use Zend\Mvc\Controller\AbstractActionController;

class MyController extends AbstractActionController
{
    public function fooAction()
    {
        if (!$this->aclAllowsAccess()) {
            // Use redirect plugin to redirect
            return $this->redirect('user/login');
        }

        // normal code flow
    }
}

An example like the PRG plugin works:

use Zend\Mvc\Controller\AbstractActionController;
use Zend\Http\Response;

class MyController extends AbstractActionController
{
    public function fooAction()
    {
        $result = $this->aclCheck();

        if ($result instanceof Response) {
            // Use return value to short-circuit
            return $result
        }

        // normal code flow
    }
}

The plugin could then look like this (in the second case):

use Zend\Mvc\Controller\Plugin\AbstractPlugin;

class AclCheck extends AbstractPlugin
{
    public function __invoke()
    {
        // Check the ACL
        if (false === $result) {
            $controller = $this->getController();
            $redirector = $controller->getPluginManager()->get('Redirect');
            $response = $redirector->toRoute('user/login');

            return $response;
        }
    }
}

In your question you say:

[...] it doesn't work as the plugin appears to require a controller.

This can be a problem inside the controller plugin when you want to do $this->getController() in the plugin. You either must extend Zend\Mvc\Controller\Plugin\AbstractPlugin or implement Zend\Mvc\Controller\Plugin\PluginInterface to make sure your ACL plugin is injected with the controller.

If you do not want this, there is an alternative you directly return a response you create yourself. It is a bit less flexible and you create a response object while there is already a response object (causing possible conflicts with both responses), but the plugin code would change like this:

use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\Http\PhpEnvironment\Response;

class AclCheck extends AbstractPlugin
{
    public function __invoke()
    {
        // Check the ACL
        if (false === $result) {
            $response = new Response;
            $response->setStatusCode(302);
            $response->getHeaders()
                     ->addHeaderLine('Location', '/user/login');

            return $response;
        }
    }
}