Zend Framework 3 error handling

安稳与你 提交于 2019-12-11 16:49:49

问题


I'm trying to handle error from my CheckAccess plugin within ZF3.

Plugin is attached like this in module's onBootstrap method

class Module {
    public function onBootstrap()
    {
        ...
        $em->attach(MvcEvent::EVENT_DISPATCH, new Plugin\CheckAccess($sm), 2);
        ...
    }
}

Here what i'm doing exactly inside the plugin's __invoke() when i need to redirect not logged-in user from some page to login page:

if ($user->isGuest()) {
        $rh = new RouteHelper($e->getRouter(), $e->getRouteMatch());
        $url = $rh->frontend('auth', 'login') . '?request=' . urlencode($request->getRequestUri());

        throw new HttpRedirectException(HttpRedirectException::SEE_OTHER_303, $url);
    } else {
        throw new HttpClientException(HttpClientException::FORBIDDEN_403);
    }
}

The problem is those exceptions collapse the application. I mean there is no any handling of such exceptions in the framework. And i didnt find any help in documentation. How can i solve that? I guess there shoud be some best practices about "correct" access checking and access-exceptions handling in zf.


回答1:


I never seen someone throw exceptions for this purpose, especially HttpRedirectException and HttpClientException so unfortunately I don't have an answer for that now.

You can achieve what you want in a much more cleaner and easier way, without the need of throwing exceptions. You can do something like:

$sharedEventManager->attach(AbstractActionController::class, 
                MvcEvent::EVENT_DISPATCH, [$this, 'onDispatch'], 100);

And then check if the user is logged in, and based on that use this redirect method:

/**
 * Event listener method for the 'Dispatch' event. We listen to the Dispatch
 * event to call the access filter. The access filter allows to determine if
 * the current visitor is allowed to see the page or not. If he/she
 * is not authorized and is not allowed to see the page, we redirect the user 
 * to the login page.
 */
public function onDispatch(MvcEvent $event)
{
    // Get controller and action to which the HTTP request was dispatched.
    $controller = $event->getTarget();
    $controllerName = $event->getRouteMatch()->getParam('controller', null);
    $actionName = $event->getRouteMatch()->getParam('action', null);

    // Convert dash-style action name to camel-case.
    $actionName = str_replace('-', '', lcfirst(ucwords($actionName, '-')));

    // Get the instance of AuthManager service.
    $authManager = $event->getApplication()->getServiceManager()->get(AuthManager::class);

    // Execute the access filter on every controller except AuthController
    // (to avoid infinite redirect).
    if ($controllerName!=AuthController::class)
    {
        $result = $authManager->filterAccess($controllerName, $actionName);

        if ($result==AuthManager::AUTH_REQUIRED) {

            // Remember the URL of the page the user tried to access. We will
            // redirect the user to that URL after successful login.
            $uri = $event->getApplication()->getRequest()->getUri();

            // Make the URL relative (remove scheme, user info, host name and port)
            // to avoid redirecting to other domain by a malicious user.
            $uri->setScheme(null)
                ->setHost(null)
                ->setPort(null)
                ->setUserInfo(null);
            $redirectUrl = $uri->toString();

            // Redirect the user to the "Login" page.
            return $controller->redirect()->toRoute('login', [], 
                    ['query'=>['redirectUrl'=>$redirectUrl]]);
        }
        else if ($result==AuthManager::ACCESS_DENIED) {
            // Redirect the user to the "Not Authorized" page.
            return $controller->redirect()->toRoute('not-authorized');
        }
    }
}

As you can see, this is just an example but it's very straight forward and you have check ACL's as well, and there is no need to throw exceptions...

However what I would do ( and I do ) to intercept all exceptions is to attach the following:

// The "init" method is called on application start-up and 
    // allows to register an event listener.
    public function init(ModuleManager $manager)
    {
        // Get event manager.
        $eventManager = $manager->getEventManager();
        $sharedEventManager = $eventManager->getSharedManager();
        // Register the event listener method. 
        $sharedEventManager->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH_ERROR,
                                    [$this, 'onError'], 100);
        $sharedEventManager->attach(__NAMESPACE__, MvcEvent::EVENT_RENDER_ERROR, 
                                    [$this, 'onError'], 100);
    }

    // Event listener method.
    public function onError(MvcEvent $event)
    {
        // Get the exception information.
        $exception = $event->getParam('exception');
        if ($exception!=null) {
            $exceptionName = $exception->getMessage();
            $file = $exception->getFile();
            $line = $exception->getLine();
            $stackTrace = $exception->getTraceAsString();
        }
        $errorMessage = $event->getError();
        $controllerName = $event->getController();

        // Prepare email message.
        $to = 'admin@yourdomain.com';
        $subject = 'Your Website Exception';

        $body = '';
        if(isset($_SERVER['REQUEST_URI'])) {
            $body .= "Request URI: " . $_SERVER['REQUEST_URI'] . "\n\n";
        }
        $body .= "Controller: $controllerName\n";
        $body .= "Error message: $errorMessage\n";
        if ($exception!=null) {
            $body .= "Exception: $exceptionName\n";
            $body .= "File: $file\n";
            $body .= "Line: $line\n";
            $body .= "Stack trace:\n\n" . $stackTrace;
        }

        $body = str_replace("\n", "<br>", $body);

        // Send an email about the error.
        mail($to, $subject, $body);
    }

This will catch all the errors and even send you an email! LOL

I hope these snippets will help you understand how to better develop using ZF3.

Credit for these snippets go to olegkrivtsov.



来源:https://stackoverflow.com/questions/49984015/zend-framework-3-error-handling

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!