Score:1

How can I return a templated response from an Exception Event subscriber?

ve flag

I'm currently using an Event Subscriber to catch exceptions and return a plain-text message response to the user (instead of the default white screen of death).

Is there a way to return a templated response (using the site's theme, similar to a Controller's response) from the event subscriber in Drupal 9/10?

Jaypan avatar
de flag
There was a method for this in D7, but it was broken between D7 and D8, and has still not been repaired. You can use the patch from this issue however: https://www.drupal.org/project/drupal/issues/2720109
4uk4 avatar
cn flag
This issue is about the Drupal 7 perspective on fatal errors. Catching an exception in an Symfony event subscriber is not a fatal error and you are in full control to set an HTML response instead of a plain-text message.
Jaypan avatar
de flag
Well, the D7 functionality in core was not properly upgraded to D8 (and still hasn't been). It's a core API that is meant to handle this issue.
4uk4 avatar
cn flag
This is not the only API change because of Symfony. I find the Symfony exception handling far more capable. Core provides HTML exception subscribers for 4xx exceptions and fall backs to plain text error messages in case of 5xx exceptions. It would be no problem to add a custom event subscribers (what the OP is trying to do) to catch 5xx exceptions and set an HTML response.
Score:2
cn flag

This depends on the exceptions you want to catch.

Example for 5xx HTTP exceptions:

namespace Drupal\mymodule\EventSubscriber;

use Drupal\Core\EventSubscriber\DefaultExceptionHtmlSubscriber;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;

/**
 * Exception subscriber for handling 5xx exceptions
 */
class MymoduleExceptionSubscriber extends DefaultExceptionHtmlSubscriber {

  protected static function getPriority() {
    return -129;
  }

  protected function getHandledFormats() {
    return ['html'];
  }

  /**
   * Handles a 5xx error for HTML.
   *
   * @param \Symfony\Component\HttpKernel\Event\ExceptionEvent $event
   *   The event to process.
   */
  public function on5xx(ExceptionEvent $event) {
    if (($exception = $event->getThrowable()) && $exception instanceof HttpExceptionInterface) {
      $this->makeSubrequest($event, '/my5xxcontroller', $exception->getStatusCode());
    }
  }

}

Example for all exceptions (only for demonstration purpose, better check for the exceptions you want to catch):

namespace Drupal\mymodule\EventSubscriber;

use Drupal\Core\EventSubscriber\DefaultExceptionHtmlSubscriber;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;

/**
 * Exception subscriber for handling all exceptions
 */
class MymoduleExceptionSubscriber extends DefaultExceptionHtmlSubscriber {

  protected static function getPriority() {
    return -129;
  }

  protected function getHandledFormats() {
    return ['html'];
  }

  /**
   * Handles all exceptions for HTML
   *
   * @param \Symfony\Component\HttpKernel\Event\ExceptionEvent $event
   *   The event to process.
   */
  public function onException(ExceptionEvent $event) {
    $request = $event->getRequest();
    $handled_formats = $this->getHandledFormats();
    $format = $request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT, $request->getRequestFormat());
    if (empty($handled_formats) || in_array($format, $handled_formats)) {
      $this->makeSubrequest($event, '/my5xxcontroller', 500);
    }
  }

}

mymodule.services.yml

services:
  mymodule.5xx_subscriber:
    class: Drupal\mymodule\EventSubscriber\MymoduleExceptionSubscriber
    tags:
      - { name: event_subscriber }
    arguments: ['@http_kernel', '@logger.channel.php', '@redirect.destination', '@router.no_access_checks']
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.