Zend framework2 手动触发404错误

Zend framework2 手动触发404错误,zend-framework2,Zend Framework2,我一直在努力从另一个控制器侦听器中手动触发dispatch.error 目标是在路由参数无效时引发404错误,并让标准Zend\Mvc\View\Http\RouteNotFoundStrategy捕获该错误,从而呈现404页面 public function FooController extends AbstractActionController { protected $foo; public function getEventManager() {

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

目标是在路由参数无效时引发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;
    }
}
事件已正确触发,调试这些事件会提供:

调用了
Zend\Mvc\View\Http\RouteNotFoundStrategy::detectNotFoundError
(错误为:
找不到错误控制器

调用
Zend\Mvc\View\Http\RouteNotFoundStrategy::prepareNotFoundViewModel
调用
Zend\Mvc\View\Http\RouteNotFoundStrategy::injectNotFoundReason

但是,返回后面的响应会给我一个没有主体的
404

return $response; // ends execution with 404 but no body, just white screen.
如果我没有返回响应,调度将继续,我将得到一个
500
错误

运行时异常“未设置foo”


如何手动触发404错误并正确呈现404模板?

我认为您不能,也不应该。当您在控制器中时,路由器已经完成了它的工作,因此返回到
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();
}

终于找到了问题所在

Zend\Mvc\Controller\AbstractController
方法期望在停止执行控制器事件之前返回
响应

这是
分派
方法的相关部分

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

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

return $e->getResult();
我需要做的是返回结果(即错误视图模型)并手动停止事件传播(以确保未执行其余的控制器操作)


适用于Zend Framework 3

手动将触发器404放入控制器并将其放入错误侦听器:

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];
}

谢谢你的意见。route参数实际上已经是必需的参数;如果没有提供,它将是404。问题是,我需要使用它加载一个对象,如果该对象无效,则使用404。这不是404,因为请求与操作匹配,并且处理请求时出现问题。404是专门为无法匹配的请求而设计的。在这种情况下,我将使用错误消息(即:flash messages)重定向HTTP响应需要是
404
。然而,我认为您对实现细节的理解可能是正确的。我不应该在
routenotfound策略中处理错误,而应该在
ExceptionStrategy
中处理错误。这将允许我捕获自定义错误,以不同的方式进行解释,并仍然返回404HTTP响应。我会回来报到的。
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];
}