feat: make server errors visible to the user for htmx requests
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\EventListener;
|
||||||
|
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
|
||||||
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
|
use Twig\Environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an error modal for unhandled exceptions on HTMX requests.
|
||||||
|
*
|
||||||
|
* Without this, a 500 from an HTMX endpoint is silently discarded by the browser
|
||||||
|
* and the user sees nothing. This listener intercepts those exceptions and returns
|
||||||
|
* a 200 with the error modal HTML, using HX-Retarget/HX-Reswap to append it to
|
||||||
|
* <body> regardless of the original hx-target.
|
||||||
|
*/
|
||||||
|
class HtmxExceptionListener implements EventSubscriberInterface
|
||||||
|
{
|
||||||
|
public function __construct(private readonly Environment $twig)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSubscribedEvents(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
KernelEvents::EXCEPTION => ['onKernelException', -10],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onKernelException(ExceptionEvent $event): void
|
||||||
|
{
|
||||||
|
if (false === $event->getRequest()->headers->has('HX-Request')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($event->hasResponse()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$html = $this->twig->render('htmx_error_modal.html.twig');
|
||||||
|
|
||||||
|
$response = new Response($html, Response::HTTP_OK);
|
||||||
|
$response->headers->set('HX-Retarget', 'body');
|
||||||
|
$response->headers->set('HX-Reswap', 'beforeend');
|
||||||
|
|
||||||
|
$event->setResponse($response);
|
||||||
|
$event->allowCustomResponseCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{% embed '_partials/_modal.html.twig' with { 'level': 'error' } %}
|
||||||
|
{% block title %}
|
||||||
|
Oops...
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% include '_partials/_alert.html.twig' with {
|
||||||
|
level: 'error',
|
||||||
|
modal: true,
|
||||||
|
messages: ['Da ist etwas schiefgelaufen. Wir haben uns Notizen gemacht und kümmern uns schnellstmöglich darum.']
|
||||||
|
} %}
|
||||||
|
{% endblock %}
|
||||||
|
{% endembed %}
|
||||||
Reference in New Issue
Block a user