Catch exception at root

Viewed 243

I'm learning Symfony5 and I was wondering whether it is possible to catch exceptions after route's execution.

The main idea is to get rid of some recurrent try-catch blocks of code in my routes:

    public function getStudentMean(int $studentId): Response
    {
        try {
            $mean = $this->gradedStudentService->getMean($studentId);
            return new Response($mean, Response::HTTP_OK);
        } catch (AbstractApiException $e) {
            return $this->returnBadRequestResponse($e->getMessage());
        }
    }

    public function deleteStudent(int $id): Response
    {
        try {
            $this->studentService->deleteStudent($id);
            return new Response('', Response::HTTP_NO_CONTENT);
        } catch (AbstractApiException $e) {
            return $this->returnBadRequestResponse($e->getMessage());
        }
    }

Do I have to edit my public\index.php file to catch the exception here? Is there another cleaner method to do so?

Thanks!

1 Answers

Yes, Symfony already have an integrated solution for that, in facts, Symfony catch every exception at root and let you manage them.

You can find information here.

How to do it

First, edit config/packages/framework.yaml and set a controller to manage all exceptions (property error_controller).

framework:
    secret: '%env(APP_SECRET)%'
    #csrf_protection: true
    #http_method_override: true

    # Enables session support. Note that the session will ONLY be started if you read or write from it.
    # Remove or comment this section to explicitly disable session support.
    session:
        handler_id: null
        cookie_secure: auto
        cookie_samesite: lax

    #esi: true
    #fragments: true
    php_errors:
        log: true

    error_controller: App\Controller\ErrorController::showAction

When an exception is thrown, this controller will get as input the initial request and the thrown exception. Here is an example:

<?php
namespace App\Controller;

use App\Exceptions\ExpiredLinkException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Throwable;

/**
 * Controller for exceptions
 */
class ErrorController extends AbstractCustomController
{

    /**
     * @param Request $request
     * @param Throwable $exception
     * @return Mixed
     * @throws Throwable
     */
    public function showAction(Request $request, Throwable $exception)
    {
        if ($exception instanceof HttpException) {
            if ($exception->getStatusCode() == Response::HTTP_UNAUTHORIZED) {
                return new RedirectResponse($_ENV['WEBSITE_BASE_URL'] . 'login?source=' . urlencode($request->getUri()));
            }
        }
        if ($exception instanceof ExpiredLinkException) {
            return $this->render('error/expired.html.twig');
        }
        if ($_ENV["APP_ENV"] == "prod") {
            if ($exception instanceof HttpException) {
                if ($exception->getStatusCode() == Response::HTTP_FORBIDDEN) {
                    return $this->render('error/403.html.twig');
                }
                if ($exception->getStatusCode() == Response::HTTP_NOT_FOUND) {
                    return $this->render('error/404.html.twig');
                }
            }
            return $this->render('error/500.html.twig');
        }
        throw $exception;
    }
}
Related