Statisches SSR in Blazor mit Formular, Flash-Message und Cache

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.