Statisches SSR in Blazor mit Formular, Flash-Message und Cache
- Matt
Ein internes Werkzeug, alles unter statischem SSR, kein Interaktivitätsmodus. Eine Seite listet Datensätze, eine andere legt einen an. Nach dem Speichern soll “Gespeichert” erscheinen, aber nicht noch einmal, wenn jemand F5 drückt. Und die Listenseite soll nicht bei jedem Aufruf die Datenbank anfassen. Drei Dinge, die einzeln trivial sind und sich zu dritt gegenseitig stören.
Das Formular prüft der Server
Unter statischem SSR läuft die Validierung nach dem Absenden auf dem Server, nicht
im Browser. Das EditForm braucht dafür ein FormName, das Modell hängt an einer
Eigenschaft mit [SupplyParameterFromForm], und ein DataAnnotationsValidator
sorgt dafür, dass eine ValidationSummary bei ungültiger Eingabe wieder mitgerendert
wird.
<EditForm Model="Input" FormName="anlegen" OnValidSubmit="Speichern">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText @bind-Value="Input.Name" />
<button type="submit">Speichern</button>
</EditForm>
@code {
[SupplyParameterFromForm]
public Model Input { get; set; } = new();
}
Gegen Overposting nehme ich hier ein eigenes Formularmodell und nicht die Entität aus der Datenbank.
Die Meldung über die Weiterleitung
Nach dem Speichern leite ich weiter (NavigationManager.NavigateTo(..., forceLoad:
true)), damit ein Neuladen nicht erneut absendet. Das ist Post-Redirect-Get. Nur
überlebt eine lokale Variable diese Weiterleitung nicht.
Bis .NET 10 hieß das, sich selbst etwas mit ITempDataDictionaryFactory und einem
Cookie zu bauen. Seit .NET 11 gibt es dafür TempData direkt in Blazor. Die
Eigenschaft trägt [SupplyParameterFromTempData], wird einmal gelesen und ist
danach weg.
@code {
[SupplyParameterFromTempData]
public string? Meldung { get; set; }
private void Speichern()
{
Meldung = "Gespeichert.";
Navigation.NavigateTo("/liste", forceLoad: true);
}
}
Der Haken steht in der Doku, wird aber leicht überlesen: TempData wird nur unter
statischem SSR befüllt. Sobald die Seite in einen Interaktivmodus wechselt, bleibt
die Eigenschaft auf ihrem Standardwert. Wer beide Modi mischt, sollte vorher wissen,
welcher Render-Modus wo greift. Der Speicher
dahinter ist ein mit Data Protection verschlüsseltes Cookie, standardmäßig
.AspNetCore.Components.TempData, und unterliegt der 4-KB-Grenze des Browsers.
Output-Caching, aber nicht auf der Formularseite
Für die Listenseite reicht AddOutputCache() in Program.cs und UseOutputCache()
in der Pipeline, dann eine Policy oder [OutputCache] auf dem Leseendpunkt. Was in
keiner der drei Anleitungen zusammen steht: die Formularseite darf man damit nicht
anfassen. Ein zwischengespeicherter Antiforgery-Token bekäme jeder Besucher gleich,
was das Token wertlos macht, und die eben gelesene Flash-Meldung würde
festgehalten und dem Nächsten vorgesetzt. Dazu kommt ein offener Fehler, bei dem
[OutputCache] auf interaktiven Blazor-Seiten das Skript aus dem Tritt bringt.
Ich cache deshalb nur die echten Leseseiten und lasse alles mit Formular,
Token oder TempData bewusst außen vor. Drei kleine Teile, deren einzige Regel
ist, dass sie sich nicht überlappen dürfen.
Quellen: Blazor-Formulare, serverseitiges State Management, Output-Caching.