If you want to extend the core exception handling, you can override the handler with a custom handler.
See also How to extend the error and exception handling.

In this example we will describe how to check user access in a controller action and throw a 403 if the user is not logged in. This is just one example of many use cases.

Let's start with the controller action. In your action, you want to check if a user is logged in, if not, throw an error.

cstm_extension/Classes/Controller/ExampleController.php

 

if ($this->feUser === null) {
  throw new LoginDeniedException('Access denied');
}

 

We defined an own Execption class for this.

cstm_extension/Classes/Exception/LoginDeniedException.php

 

<?php
namespace Vendor\CstmExtension\Exception;

class LoginDeniedException extends \Exception
{
}

 

There is an exception handler at content element level. This Typoscript config allows you to override this handler.

cstm_extension/Configuration/TypoScript/setup.typoscript

 

config.contentObjectExceptionHandler = Vendor\CstmExtension\Error\Frontend\ProductionExceptionHandler

 

In your handler, you can catch the LoginDeniedException and do whatever you want, such as redirecting to the login page as shown here.

cstm_extension/Classes/Error/Frontend/ProductionExceptionHandler.php

 

<?php
class ProductionExceptionHandler extends \TYPO3\CMS\Frontend\ContentObject\Exception\ProductionExceptionHandler
{
    use ExceptionHandlerTrait;

    public function handle(
        \Exception $exception, 
        AbstractContentObject $contentObject = null, 
        $contentObjectConfiguration = []): string
    {
        if ($exception instanceof LoginDeniedException) {
            $request = $GLOBALS['TYPO3_REQUEST'];
            header('Location: ' . $this->getLoginPage((string)$request->getUri()), true, 302);
            exit;
        }
        return parent::handle($exception, $contentObject, $contentObjectConfiguration);
    }
}

 

It's a good habit to define the login page in the errorHandling section of your site config.

config/sites/yoursite/config.yaml

 

errorHandling:
  -
    errorCode: 403
    errorHandler: Page
    errorContentSource: 't3://page?uid=3427'

 

The getLoginPage function finally reads the config and builds a valid link to the target page.

cstm_extension/Classes/Error/ExceptionHandlerTrait.php

 

<?php
trait ExceptionHandlerTrait
{
    protected function getLoginPage(string $url): string
    {
        // default is to root page
        $link = '/';

        // loads the site config as array
        $request = $GLOBALS['TYPO3_REQUEST'];
        $site = $request->getAttribute('site')->getConfiguration();

        // check and loop if there is a 403 handler
        if (!empty($config['errorHandling'])) {
            foreach ($config['errorHandling'] as $errorHandler) {
                if (!empty($errorHandler['errorCode']) && !empty($errorHandler['errorContentSource']) 
                                && $errorHandler['errorCode'] == 403) {
                    
                    // add the calling url as return attribute
                    $signed = $this->signUrl($url);
                    
                    // use the typolink renderer to get a valid url
                    $contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
                    $link = $contentObject->typoLink_URL([
                        'parameter' => $errorHandler['errorContentSource'],
                        'additionalParams' => '&return_url=' . $signed,
                    ]);
                }
            }
        }
        return $link;
    }

    protected function signUrl(string $url): string
    {
        return sprintf(
            '%s@%s',
            $url,
            GeneralUtility::hmac($url, FrontendUserAuthentication::class)
        );
    }
}