We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Multiple notFound handlers

Okay this is the second time I've rewritten this, so I'm going to keep it short. I'm writing an API for my website, and I need to handle 404 errors and output them as JSON, rather than rendering a HTML view. Does anyone know how I could do this? Thank you for your time.

My handlers are defined like so:

    \Controllers\ErrorController::notFound();       // This should output HTML
    \Controllers\Api\ErrorController::notFound();   // This should output JSON

My routing is defined like so:

    $router = new \Phalcon\Mvc\Router(false);
    $router->setDefaultNamespace('Controllers');
    $router->notFound("Error::notFound");
    $router->mount(new \Routes\Api());

My API routes class is defined like so:

    namespace Routes;

    use Phalcon\Mvc\Router\Group;

    class Api extends Group
    {
        public function initialize()
        {
            // Prefixes for the standard routes
            $this->setPrefix('/api');
            $this->setPaths(["namespace" => 'Controllers\Api']);

            // How do I handle a custom 404 error here?
            // This doesn't seem to be possible
            $this->notFound("Error::notFound"); 
        }
    }

Just create one controller for notfound. Lik ErrorController. And in it you should have htmlAction and jsonAction and errorAction. errorAction just will figure it if you should forward to jsonAction or htmlAction and that's it. I would do it like this.

edited May '16
 //Routes notFound handler
        //NOTE: this handler is triggered only when part of the defined route pattern (route group) matches
        $router->notFound(['controller' => 'error', 'action' => 'notFound']);

and:

  // Attach a listener to the dispatcher service

    $eventsManager->attach('dispatch:beforeException', function ($event, $dispatcher, $exception) {
        // Handle 404 exceptions, this is real 404 not found error, it will trigger 404 http status code
      if ($exception instanceof \Phalcon\Mvc\Dispatcher\Exception) {
          $dispatcher->forward(['controller' => 'error', 'action' => 'notFound']);
          return false; //returning false is mandatory
      }

Oh so routes group can have own notFound ? Didn't know about it. Soe you already know your answer. You need to delete this - $router->notFound("Error::notFound"); and move non-api routes to group as i understand it correctly.

It doesn't exist as far as I know, altough it was something I did expect

// How do I handle a custom 404 error here?
// This doesn't seem to be possible

Oh so routes group can have own notFound ? Didn't know about it. Soe you already know your answer. You need to delete this - $router->notFound("Error::notFound"); and move non-api routes to group as i understand it correctly.



4.1k
Accepted
answer
edited May '16

I ended up using the following code, which seemed to work out pretty well:

Services

    $router = new \Phalcon\Mvc\Router(false);
    $router->setDefaultNamespace('Controllers');
    $router->notFound("Error::notFound");

ErrorController

    class ErrorController extends ControllerBase
    {
        /**
         * Redirect notFound requests to the correct namespace
         * @return bool
         */
        public function beforeExecuteRoute()
        {
            // Get the URI in parts
            $uri = array_values(array_filter(explode("/", $this->router->getRewriteUri())));

            // Check if the request is an API call
            if (isset($uri[0]) && $uri[0] === "api") {
                return $this->dispatcher->forward([
                    "namespace"  => 'Controllers\Api'
                ]);
            } else {
                return true;
            }
        }

        /**
         * Not Found handler, 404 status code
         * @return void
         */
        public function notFoundAction()
        {
            $this->response->setStatusCode(404, 'Not Found');
        }
    }
edited May '16

Oh so routes group can have own notFound ? Didn't know about it. Soe you already know your answer. You need to delete this - $router->notFound("Error::notFound"); and move non-api routes to group as i understand it correctly.

Yes, check example I provided. It's working on my production site. One handler will handle /my-uri-route-missing-part-of-uri and other will simply handle every 404 as regular, i.e. /giveMeLinkWhichDoesNotExistHere123

From the UX perspective, it is important to handle both type of errors as a nice looking not found page on your site.