如何通过事件结束动作的执行

smo*_*aim 1 zend-framework2

在我的 ZF2 应用程序中,我添加了以下事件侦听器,但是我想让操作的执行实际停止,但这不会发生。

 public function setEventManager(EventManagerInterface $events)
    {
        parent::setEventManager($events);

        $controller = $this;
        $events->attach('dispatch', function ($e) use ($controller) {
            $request = $e->getRequest();
            $method = $request->getMethod();
            $headers = $request->getHeaders();

            // If we get here, based on some conditions, the original intended action must return a new JsonViewModel()...
            return new JsonViewModel([]); // However, this doesn't do anything.
        }, 100); // execute before executing action logic
    }
Run Code Online (Sandbox Code Playgroud)

Jur*_*man 5

根据您的评论,我假设您正在进行某种身份验证。您可以完美地使用事件管理器。但是,我不会将侦听器绑定到单个控制器。如果您的 API 增加,您可能希望将 API 拆分到多个控制器上,并且您的身份验证会遇到麻烦。

我的解决方案是创建一个监听器来监听 Zend\Mvc\Application 的调度事件。这是在控制器本身的事件之前触发的事件。

use Zend\Mvc\MvcEvent;

public function onBootstrap(MvcEvent $e)
{
    $app = $e->getApplication();
    $em  = $app->getEventManager();
    $sm  = $app->getServiceManager()->getSharedManager();

    $listener   = new Listener\Authentication();
    $identifier = 'MyModule\Controller\ApiController';

    $em->attach($identifier, MvcEvent::EVENT_DISPATCH, $listener, 1000);
}
Run Code Online (Sandbox Code Playgroud)

这样,侦听器将附加到所有以 标识的控制器上MyModule\Controller\ApiController。监听器将在这些控制器的每次调度调用上被触发。如果您需要,您的侦听器可以使整个调度循环短路:

use Zend\Http\Request as HttpRequest;
use Zend\Mvc\MvcEvent;
use Zend\Json\Json;
use Zend\View\Model\JsonModel;

class Authentication
{
    public function __invoke(MvcEvent $e)
    {
        $request = $e->getRequest();

        if (!$request instanceof HttpRequest) {
            // Don't run on CLI requests
            return;
        }

        if ($result->isValid()) {
            // Say you get auth result and all is OK
            return;
        }

        // Case 1: short-circuit and return response, this is the fast way

        // The response I use here is the HTTP problem API
        $message  = array(
            'httpStatus' => 401,
            'title'      => 'Unauthorized',
            'detail'     => 'You are unauthorized to perform this request',
        );
        $response = $e->getResponse();
        $response->setStatusCode(401);
        $response->getHeaders()->addHeaderLine('Content-Type', 'application/json');
        $response->setContent(Json::encode($message);
        return $response;

        // Case 2: don't short circuit and stop propagation, you're using the JSON renderer

        $e->getResponse()->setStatusCode(401);
        $message  = array(
            'httpStatus' => 401,
            'title'      => 'Unauthorized',
            'detail'     => 'You are unauthorized to perform this request',
        );
        $model = new JsonModel($message);
        return $model;
    }
}
Run Code Online (Sandbox Code Playgroud)

我建议您使用第一种方法(自己返回响应),因为您将缩短完整的调度过程并跳过请求的完整完成。如果您真的依赖视图和响应发送者,请使用第二种情况。

现在,如果您需要通过此系统进行身份验证的控制器,请将标识符添加到此控制器:

namespace MyModule\Controller;

use Zend\Mvc\Controller\AbstractActionController;

class MyFooBarApiController extends AbstractActionController
{
    protected $eventIdentifer = 'MyModule\Controller\ApiController';

    // your code here
}
Run Code Online (Sandbox Code Playgroud)

如果您需要在未经验证的情况下允许某些请求(我总是使用白名单!),您可以在您的侦听器中执行此操作:

use Zend\Mvc\Route\RouteMatch;

$routematch = $e->getRouteMatch();
if (!$routematch instance of RouteMatch) {
    // It's a 404, don't perform auth
    return;
}
$route = $routematch->getMatchedRouteName();

if (
    ($request->isPost() && 'foo/bar' === $route) 
 || ($request->isGet() &&  'baz/bat' === $route)
) {
    // We allow these requests to pass on without auth
    return;
}
Run Code Online (Sandbox Code Playgroud)

在您的代码中,您可以明确检查请求方法和路由名称。如果需要路由参数,可以使用$routematch->getParam('id').