2014-08-29 94 views
4

我一直在努力从另一个控制器监听器中手动触发dispatch.error手动触发404错误

目标是提出一个404错误,其中路由参数无效,并使其被标准Zend\Mvc\View\Http\RouteNotFoundStrategy“抓住”,以便呈现404页面。

public function FooController extends AbstractActionController 
{ 
    protected $foo; 

    public function getEventManager() 
    { 
     $em = parent::getEventManager(); 

     // Attach with higher priority than dispatched indexAction 
     $em->attach('dispatch', array($this, 'getFooFromRouteParam'), 10); 
    } 

    public function indexAction() 
    { 
     $foo = $this->getFoo(); // Error 500 (not 400) 
    } 

    public function getFooFromRouteParam(EventInterface $event) 
    { 
     $id = $this->params('id', false); 

     if (! empty($id)) { 

      $foo = $this->aDatabaseService->loadFromTheDatabase($id); 

      if ($foo instanceof Foo) { 

       $this->setFoo($foo); 
       return; 
      } 
     } 

     $response = $event->getResponse(); 

     $response->setStatusCode(404); 
     $event->setError(\Zend\Mvc\Application::ERROR_ROUTER_NO_MATCH); 

     $event->getApplication() 
       ->getEventManager() 
       ->trigger('dispatch.error', $event); 

     //return $response; 
    } 

    public function getObjectFoo() 
    { 
     if (null == $this->foo) { 
      throw new \RuntimeException('foo not set'); 
     } 
     return $this->foo; 
    } 

    public fucntion setObjectFoo(Foo $object) 
    { 
     $this->foo = $object; 
    } 
} 

事件被正确触发,调试他们给了我:

CALLEDZend\Mvc\View\Http\RouteNotFoundStrategy::detectNotFoundError(错误是:error-controller-not-found

CALLEDZend\Mvc\View\Http\RouteNotFoundStrategy::prepareNotFoundViewModel CALLEDZend\Mvc\View\Http\RouteNotFoundStrategy::injectNotFoundReason

但是,返回后的回应给我一个404没有身体。

return $response; // ends execution with 404 but no body, just white screen. 

如果我不回调度继续响应,我得到一个500错误

RuntimeException的“富不设置”

我如何可以手动触发一个404错误,正确渲染404模板?

回答

1

终于成功地找出问题。

Zend\Mvc\Controller\AbstractController方法预计将返回一个Response它将停止执行控制器的事件。

这是dispatch方法的相关部分。

$result = $this->getEventManager()->trigger(MvcEvent::EVENT_DISPATCH, $e, 
    function ($test) { 
     return ($test instanceof Response); 
    } 
); 

if ($result->stopped()) { 
    return $result->last(); 
} 

return $e->getResult(); 

我需要什么做的是回到我的结果(这是错误的视图模型)和手动停止事件传播(以确保剩余的控制器操作未执行)

// Set the event's 'error' vars 
$event->setError(Application::ERROR_EXCEPTION); 
$event->setParam('exception', new \Exception('A custom error message here')); 

// Trigger the dispatch.error event 
$event->getApplication() 
     ->getEventManager() 
     ->trigger(MvcEvent::EVENT_DISPATCH_ERROR, $event); 

// The missing piece! Required to ensure the remaining dispatch 
// listeners are not triggered 
$event->stopPropagation(true); 

// Contains the view model that was set via the ExceptionStrategy 
return $event->getResult(); 
1

我不认为你可以,也不应该。当你在控制器中时,路由器已经完成了它的工作,所以回到Zend\Mvc\View\Http\RouteNotFoundStrategy将不起作用。

你应该做的是使路由中所需的参数,所以路由器会为你做检查。

如果您已经考虑过这个,但是仍然要使用请求处理错误404响应,看看实际DispatchListener如何处理这个吧:

$event->setError($application::ERROR_EXCEPTION) 
     ->setController($controllerName) 
     ->setParam('exception', $exception); 

$events = $application->getEventManager(); 
$results = $events->trigger(MvcEvent::EVENT_DISPATCH_ERROR, $event); 
$return = $results->last(); 
if (! $return) { 
    $return = $event->getResult(); 
} 
+0

感谢您的意见。路由参数实际上已经是必需的参数;如果没有提供,它将会404。问题是我需要使用它加载一个对象,如果该对象无效,那么404。 – AlexP 2014-08-29 10:10:49

+1

这不是404,因为请求匹配一个动作,并且处理请求时出现问题。 404是专门为无法匹配的请求而设计的。 在这种情况下,我会重定向一个错误消息(即:flash消息) – NDM 2014-08-29 10:17:38

+0

HTTP响应需要为'404'。不过,我认为你可能是正确的实施细节。而不是处理'RouteNotFoundStrategy'内的错误,我应该在'ExceptionStrategy'内执行。这将允许我捕获自定义错误,以不同方式解释并仍返回404 HTTP响应。我会回报。 – AlexP 2014-08-29 10:43:09

1

对于Zend框架3

TRIGER 404 manuely在控制器并把它在错误监听:

public function articleAction() 
{ 
    // For eg. if we can not found article fetched by url_slug 
    if(!$article){ 
     $this->getResponse()->setStatusCode(404); 
     $this->getEvent()->setName(MvcEvent::EVENT_DISPATCH_ERROR); 
     $this->getEvent()->getApplication()->getEventManager()->triggerEvent($this->getEvent()); 

     return false; 
    } 

    return ['article' => $article]; 
}