public function handle()
    {
        try {
            $query = $this->di->request->getRoute();
            $parts = $this->di->request->getRouteParts();

            // Match predefined routes
            foreach ($this->routes as $route) {
                if ($route->match($query)) {
                    return $route->handle();
                }
            }

            // Default handling route as :controller/:action/:params using the dispatcher
            $dispatcher = $this->di->dispatcher;
            $dispatcher->setControllerName(isset($parts[0]) ? $parts[0] : 'index');

            if ($dispatcher->isValidController()) {
                $dispatcher->setActionName(isset($parts[1]) ? $parts[1] : 'index');

                $params = [];
                if (isset($parts[2])) {
                    $params = $parts;
                    array_shift($params);
                    array_shift($params);
                }
                $dispatcher->setParams($params);

                if ($dispatcher->isCallable()) {
                    return $dispatcher->dispatch();
                }
            }

            // Use the "catch-all" route
            if ($this->defaultRoute) {
                return $this->defaultRoute->handle();
            }

            // No route was matched
            $this->handleInternal('404');
        } catch (\Exception $e) {
            // Exception codes can match a route for a http status code
            $code = $e->getCode();
            $statusCodes = [403, 404, 500];
            if (in_array($code, $statusCodes)) {
                $this->di->flash->setMessage($e->getMessage());
                $this->handleInternal($code);
            } else {
                throw $e;
            }
        }
    }