Symfony Controller Attribute wie #[IsCsrfTokenValid], #[IsSignatureValid], #[Lock] und #[RateLimit] nehmen euren Controllern viel Arbeit ab. Bisher hatten sie aber eine harte Seite. Schlägt eine Prüfung fehl, sieht der Nutzer eine Fehlerseite. Mit Symfony 8.2 ändert sich das. Der Controller kann den Fehler selbst abfangen und eine klare Meldung zeigen. Wir betreiben Symfony täglich in Production, auch dieser Blog gehört zu einem Projekt auf dem Symfony CMS Sulu. Hier zeigen wir, wie das Feature funktioniert, wo die Falle liegt und wie ihr es sauber testet. Alle anderen Neuerungen der Version stehen in Symfony 8.2 neue Features erklärt.
Symfony Code im Review?
Wir prüfen eure Controller mit eurem Team, sichern Fehlerfälle mit PHPUnit und Cypress ab und bringen die Anwendung auf Symfony 8.2.
Was Controller Attribute in Symfony prüfen
Ein Controller Attribut steht direkt über der Methode. Symfony wertet es aus, bevor der Controller läuft. So bleibt der eigentliche Code frei von Prüfungen, die sich in jedem Projekt wiederholen. Vier dieser Attribute sind für das neue Feature wichtig.
| Attribut | Was es prüft | Exception bei Fehler |
|---|---|---|
| #[IsCsrfTokenValid] | Kommt das Formular wirklich von eurer Seite | InvalidCsrfTokenException |
| #[IsSignatureValid] | Ist ein signierter Link echt und noch gültig | SignedUriException |
| #[Lock] | Läuft die Aktion gerade schon in einem anderen Request | LockConflictedException |
| #[RateLimit] | Hat der Nutzer sein Limit an Anfragen erreicht | RateLimitExceededException |
Bisher endete jeder dieser Fehler gleich. Symfony warf die Exception, der Nutzer bekam eine Fehlerantwort. Für eine API ist das genau richtig. Der Client bekommt einen Statuscode und reagiert darauf.
Warum Fehlerseiten in HTML Anwendungen stören
In einer normalen Webanwendung sitzt ein Mensch vor dem Bildschirm. Stellt euch eine Registrierung vor. Die Bestätigungsmail liegt drei Tage im Postfach, dann klickt jemand auf den Link. Die Signatur ist abgelaufen. Statt einer Fehlerseite wäre ein Satz viel hilfreicher: Der Link ist abgelaufen, wir schicken dir einen neuen.
Das gleiche gilt für einen CSRF Token nach langer Pause im Formular oder für ein Rate Limit beim Löschen vieler Einträge. Bis Symfony 8.1 hattet ihr dafür nur einen Weg. Ihr musstet das Attribut entfernen und die Prüfung von Hand im Controller schreiben. Genau dieser Code ist fehleranfällig und wird im Review gern übersehen.
So fängt der Controller den Fehler in Symfony 8.2 ab
Symfony 8.2 löst das mit einem einfachen Trick. Ihr ergänzt im Controller ein nullable Argument, dessen Typ die passende Exception ist. Schlägt die Prüfung fehl, übergibt Symfony die Exception an dieses Argument und ruft den Controller trotzdem auf. Läuft alles glatt, ist das Argument null.
use Symfony\Component\HttpFoundation\Exception\SignedUriException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\IsSignatureValid;
#[IsSignatureValid]
public function verifyEmail(?SignedUriException $signatureError = null): Response
{
if (null !== $signatureError) {
$this->addFlash('error', 'Der Link ist ungültig oder abgelaufen.');
return $this->redirectToRoute('app_login');
}
// Mail Adresse bestätigen
}
Das Attribut bleibt also stehen. Die Prüfung macht weiter Symfony, ihr entscheidet nur, was bei einem Fehler passiert. Controller ohne solches Argument verhalten sich genau wie vorher. Für bestehende Anwendungen ändert sich nichts, bis ihr das Argument bewusst ergänzt.
Mehrere Attribute mit Union Types abfangen
Der Typ des Arguments entscheidet, welche Fehler im Controller ankommen. Hängen mehrere Attribute an einer Aktion, fasst ein Union Type sie zusammen. Mit match bekommt jeder Fehler seine eigene Meldung:
#[IsCsrfTokenValid('delete-post')]
#[RateLimit('delete_post')]
public function delete(
Post $post,
InvalidCsrfTokenException|RateLimitExceededException|null $failure = null,
): Response {
if (null !== $failure) {
$this->addFlash('error', match (true) {
$failure instanceof InvalidCsrfTokenException => 'Deine Sitzung ist abgelaufen. Bitte versuche es noch einmal.',
$failure instanceof RateLimitExceededException => 'Zu viele Löschungen. Bitte versuche es später noch einmal.',
});
return $this->redirectToRoute('post_show', ['id' => $post->getId()]);
}
// Post löschen
}
Nur bestimmte Fehler mit Unterklassen behandeln
Manchmal wollt ihr nicht jeden Fehler abfangen. Ein abgelaufener Link ist ein normaler Fall im Alltag. Ein manipulierter Link ist etwas ganz anderes. Dafür nutzt ihr eine Unterklasse als Typ. ?ExpiredSignedUriException ist eine Unterklasse von SignedUriException und fängt nur abgelaufene Links ab.
#[IsSignatureValid]
public function verifyEmail(?ExpiredSignedUriException $expired = null): Response
{
if (null !== $expired) {
$this->addFlash('info', 'Der Link ist abgelaufen. Wir haben dir einen neuen geschickt.');
return $this->redirectToRoute('app_login');
}
// ...
}
So bleibt der Typ eine klare Aussage im Code. Wer die Methode liest, sieht sofort, welche Fehler hier freundlich behandelt werden.
Vorher und nachher im direkten Vergleich
| Situation | Bis Symfony 8.1 | Ab Symfony 8.2 |
|---|---|---|
| Signierter Link abgelaufen | Exception und Fehlerseite | Flash Meldung und Weiterleitung direkt im Controller |
| CSRF Token ungültig | Exception und Fehlerseite | Hinweis auf abgelaufene Sitzung, Nutzer versucht es erneut |
| Rate Limit erreicht | Exception und Fehlerseite | Eigene Meldung mit Bitte um späteren Versuch |
| Lock bereits belegt | Exception und Fehlerseite | Eigene Meldung statt Abbruch |
| Prüfung mit eigenem Fallback | Attribut entfernen und Prüfung selbst schreiben | Attribut bleibt, ein nullable Argument reicht |
| Eigene Attribute | Kein Standardweg | Über das Attribut #[RecoverableAttribute] |
| #[IsGranted] | Exception und Fehlerseite | Unverändert, Fehler kommen bewusst nie im Controller an |
Warum IsGranted seine Fehler nie weitergibt
Hier liegt die wichtigste Regel des Features. Bekommt der Controller die Exception, läuft er trotzdem weiter. Fehlt die Prüfung auf null, führt er die Aktion aus, obwohl der Check fehlgeschlagen ist. Beim CSRF Beispiel oben würde der Post also trotz ungültigem Token gelöscht.
Genau deshalb macht Symfony bei #[IsGranted] eine Ausnahme. Das Attribut reicht seine Fehler nie an den Controller weiter. Eine vergessene Prüfung würde sonst Nutzern Aktionen erlauben, für die sie keine Rechte haben. Für alle anderen Attribute gilt: Wer das Argument ergänzt, muss es auch prüfen. Das gehört in jede Review Checkliste.
Eigene Attribute mit RecoverableAttribute
Viele Teams haben eigene Controller Attribute, etwa für Feature Flags oder Mandantenprüfungen. Mit dem neuen Attribut #[RecoverableAttribute] bekommen sie dasselbe Verhalten wie die eingebauten Attribute. Wie ihr das einrichtet, beschreibt die Symfony Dokumentation zu Controller Attribute Failures. Prüft vorher ehrlich, ob euer Attribut eine Sicherheitsprüfung ist. Dann gilt dieselbe Überlegung wie bei #[IsGranted].
Das Feature stammt von Kevin Bond. Die Umsetzung könnt ihr im Pull Request 67563 auf GitHub nachlesen. Javier Eguiluz stellt es im Symfony Blogartikel zu Controller Attribute Failures vor.
Fehlerfälle in Symfony richtig testen
Sobald ein Controller seine Fehler selbst behandelt, braucht jeder Fehlerfall einen eigenen Test. Ein Functional Test schickt einen ungültigen Token und prüft zwei Dinge. Die Weiterleitung stimmt, und die Aktion ist nicht passiert.
public function testDeleteWithInvalidCsrfTokenKeepsPost(): void
{
$client = static::createClient();
$client->request('POST', '/post/1/delete', ['_token' => 'invalid']);
self::assertResponseRedirects('/post/1');
// prüfen, dass der Post weiterhin in der Datenbank steht
}
Der zweite Teil ist der entscheidende. Eine korrekte Weiterleitung allein beweist nicht, dass die null Prüfung im Controller steht. Erst der Blick in die Datenbank zeigt, dass nichts gelöscht wurde. Wie Tests mit echtem Service Container aussehen, zeigt der Symfony KernelTestCase. Was es in PHPUnit gerade Neues gibt, steht in PHPUnit 13.4 mit Sebastian Bergmann.
Ob die Flash Meldung beim Nutzer wirklich sichtbar ist, prüft am besten ein E2E Test im Browser. Abgelaufene Links und lange Pausen im Formular lassen sich dort gut nachstellen. Unser Cypress E2E Testing Setup deckt genau solche Abläufe ab.
Was das Feature für KI generierten Code bedeutet
Coding Agents schreiben Controller schnell und sauber aussehend. Eine fehlende null Prüfung fällt beim Lesen kaum auf, der Code wirkt vollständig. Bei diesem Feature ist das ein echtes Risiko, weil der Controller nach einem Fehler weiterläuft. Deshalb gehören zwei Regeln in eure Vorgaben für KI: Jedes abgefangene Attribut braucht eine Prüfung direkt am Anfang der Methode. Und jeder abgefangene Fehler braucht einen Test, der beweist, dass die Aktion nicht ausgeführt wurde. Solche Regeln halten wir in den NCA PHP AI Coding Guidelines fest.
Controller Attribute beim Update auf Symfony 8.2 aufräumen
Das Feature bricht keinen bestehenden Code. Es lohnt sich trotzdem, beim Update gezielt zu suchen:
- Handgeschriebene Prüfungen finden: Controller, die CSRF Tokens, Signaturen, Locks oder Limits selbst prüfen, weil sie eine eigene Meldung brauchten.
- Zurück zum Attribut: Diese Prüfungen durch das passende Attribut und ein nullable Argument ersetzen.
- Typ bewusst wählen: Nur die Fehler abfangen, die wirklich eine freundliche Meldung brauchen, notfalls mit Unterklassen.
- null Prüfung reviewen: Jede abgefangene Exception muss ganz oben im Controller geprüft werden.
- Tests ergänzen: Pro Fehlerfall ein Functional Test, dazu ein E2E Durchlauf für die sichtbare Meldung.
Viele Deprecations rund um das Update löst Rector PHP automatisch. Den Gesamtüberblick über Architektur, Versionen und alle 8.2 Features findet ihr im Glossar zum Symfony Framework. Und wenn es schon brennt, hilft die NCA Soforthilfe für Webprojekte.
Fazit
Mit Symfony 8.2 werden Controller Attribute auch für HTML Anwendungen richtig angenehm. Die Prüfung bleibt im Attribut, die Reaktion auf einen Fehler liegt im Controller. Nutzer sehen eine klare Meldung statt einer Fehlerseite. Der Preis ist eine neue Verantwortung im Code. Wer die Exception annimmt, muss sie auch prüfen. Mit einer festen Review Regel und je einem Test pro Fehlerfall ist das Feature ein echter Gewinn für jedes Symfony Projekt.
Häufige Fragen zu Symfony Controller Attributen
Was ist 2026 neu bei Controller Attributen in Symfony 8.2?
Controller können Fehler aus den Attributen IsCsrfTokenValid, IsSignatureValid, Lock und RateLimit selbst abfangen. Dafür ergänzt ihr ein nullable Argument mit dem Typ der passenden Exception. Schlägt die Prüfung fehl, übergibt Symfony die Exception an den Controller, statt direkt eine Fehlerseite zu zeigen.
Welche Attribute unterstützen 2026 das Abfangen von Fehlern?
Unterstützt werden IsCsrfTokenValid mit InvalidCsrfTokenException, IsSignatureValid mit SignedUriException, Lock mit LockConflictedException und RateLimit mit RateLimitExceededException. Eigene Attribute bekommen das Verhalten über das neue Attribut RecoverableAttribute. IsGranted ist bewusst ausgenommen.
Warum funktioniert das Feature 2026 nicht mit IsGranted?
Bekommt ein Controller die Exception, läuft er trotzdem weiter. Fehlt die Prüfung im Code, würde er die Aktion ausführen. Bei IsGranted hieße das, Nutzer könnten Aktionen ohne Berechtigung auslösen. Deshalb gibt IsGranted seine Fehler nie an den Controller weiter.
Muss ich 2026 beim Update auf Symfony 8.2 Controller ändern?
Nein. Controller ohne das neue Argument verhalten sich genau wie vorher. Das Feature greift erst, wenn ihr bewusst ein nullable Argument mit dem Exception Typ ergänzt. Es lohnt sich aber, handgeschriebene Prüfungen zu suchen und wieder durch das passende Attribut zu ersetzen.
Wie teste ich 2026 abgefangene Attribut Fehler?
Schreibt pro Fehlerfall einen Functional Test, der einen ungültigen Token oder abgelaufenen Link schickt. Prüft die Weiterleitung und vor allem, dass die Aktion nicht ausgeführt wurde, etwa per Blick in die Datenbank. Ein E2E Test im Browser zeigt zusätzlich, ob die Meldung beim Nutzer ankommt.
Symfony Projekte mit Never Code Alone
Wir bringen Symfony und Sulu Anwendungen mit eurem Team auf den aktuellen Stand: Update auf Symfony 8.2, Rector, PHPStan, PHPUnit, Cypress E2E Tests und saubere Pipelines. Kostenloses Kennenlernen, ehrliche Aufwandsschätzung, minutengenaue Abrechnung.
