2016-09-05 14 views
0

在我ZF2应用程序,我有时会抛出一个异常LoginRequired。我想捕捉这个异常并使用它将用户重定向到登录页面。我应该怎么做?ZF2:侦听一个MVC异常,然后重定向

的错误通常是由我的服务层抛出。它通常在调度过程中被抛出,但在渲染过程中也可能被抛出,因为有一个视图助手调用某些服务层方法(例如获取当前用户)。

我原以为这是一个简单的事情,使用模块的引导方法将听众附加到dispatch.errorrender.error事件。然后我打算测试一个LoginRequired异常,但我似乎无法掌握实际的异常情况。 ($mvcEvent->getResult()->exception返回null)。

回答

1

我有一个应该有所帮助的示例监听器聚合。

不同的是我的侦听器使用的配置和与之匹配的路由名称,以确定是否应该进行重定向。您可以改为在dispatch.error期间使用$mvcEvent->getParam('exception')从MVC事件中获取异常并检查。

use ArpAuth\Service\AuthenticationService; 
use ArpAuth\Service\AuthenticationServiceAwareTrait; 
use Zend\Console\Console; 
use Zend\EventManager\EventManagerInterface; 
use Zend\Http\Response; 
use Zend\Mvc\MvcEvent; 
use Zend\Mvc\Router\RouteMatch; 
use Zend\EventManager\AbstractListenerAggregate; 


class NoAuthRedirectStrategy extends AbstractListenerAggregate 
{ 

    protected $whitelistRoutes = []; 
    protected $redirectRoute = 'user/auth/login'; 

    use AuthenticationServiceAwareTrait; 

    public function __construct(AuthenticationService $authenticationService, array $options = []) 
    { 
     $this->authenticationService = $authenticationService; 

     if (! empty($options)) { 
      $this->setOptions($options); 
     } 
    } 


    public function attach(EventManagerInterface $eventManager) 
    { 
     $eventManager->attach(MvcEvent::EVENT_DISPATCH, [$this, 'isAuthenticated'], 1000); 
    } 

    public function isAuthenticated(MvcEvent $event) 
    { 
     $routeMatch = $event->getRouteMatch(); 

     if (Console::isConsole() || ! $routeMatch instanceof RouteMatch) { 
      return; 
     } 

     $serviceManager = $event->getApplication()->getServiceManager(); 
     $currentRoute = $routeMatch->getMatchedRouteName(); 
     $isRemoteApiCall = $serviceManager->get('IsRemoteApiCall'); 

     if ($this->hasWhitelistRoute($currentRoute) || $isRemoteApiCall || $this->authenticationService->hasIdentity()) { 
      return; 
     } 

     $router = $event->getRouter(); 
     /** @var Response $response */ 
     $response = $event->getResponse(); 

     $url = $router->assemble([], ['name' => $this->redirectRoute]); 

     $response->getHeaders()->addHeaderLine('Location', $url); 
     $response->setStatusCode(302); 

     return $response; 
    } 

    public function setRedirectRoute($redirectRoute) 
    { 
     $this->redirectRoute = $redirectRoute; 

     return $this; 
    } 

    public function hasWhitelistRoute($route) 
    { 
     return (! empty($route) && in_array($route, $this->whitelistRoutes)); 
    } 

    public function getWhitelistRoutes() 
    { 
     return $this->whitelistRoutes; 
    } 

    public function setWhitelistRoutes(array $routes) 
    { 
     $this->whitelistRoutes = []; 

     return $this->addWhitelistRoutes($routes); 
    } 

    public function addWhitelistRoutes(array $routes) 
    { 
     foreach($routes as $route) { 
      $this->addWhitelistRoute($route); 
     } 
     return $this; 
    } 

    public function addWhitelistRoute($route) 
    { 
     if (! $this->hasWhiteListRoute($route)) { 
      $this->whitelistRoutes[] = $route; 
     } 

     return $this; 
    } 

    public function setOptions(array $options) 
    { 
     if (isset($options['whitelist']) && is_array($options['whitelist'])) { 
      $this->setWhitelistRoutes($options['whitelist']); 
     } 

     if (isset($options['redirect_route'])) { 
      $this->setRedirectRoute($options['redirect_route']); 
     } 

     return $this; 
    } 
} 
+0

谢谢AlexP。你怎么知道'异常'是一个事件参数?这真的很有帮助。 –

+1

@kim退房[“调度听众”](https://github.com/zendframework/zend-mvc/blob/master/src/DispatchListener.php#L131),这是用于分派控制器的主要事件侦听器。 – AlexP