Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 9 Min.

Blazor SSR statt MVC und Razor Pages

In .NET 8.0 hat Microsoft das Einsatzspektrum von Blazor auf Server-Side-Rendering erweitert.
Blazor gab es schon vor dem Erscheinen von .NET 8.0 in verschiedenen Varianten:
  • Blazor Server und Blazor WebAssembly für Single-Page-Web-Apps,
  • Blazor Desktop für Hybrid-Apps auf Windows,
  • Blazor MAUI für Hybrid-Apps auf Windows, macOS, iOS und Android.
Dabei werden letztere beiden Varianten auch mit dem Oberbegriff Blazor Hybrid zusammengefasst, siehe Bild 1. Seit .NET 8.0 gibt es zwei weitere Blazor-Architekturmodelle:
Blazor-Varianten und Blazor-Projektvorlagen vor und nach .NET 8.0 (Bild 1) © Autor
  • Blazor Server-Side-Rendering (auch Static Server-Side-Rendering genannt, abgekürzt in beiden Fällen mit SSR) für Multi-Page-Web-Apps,
  • Blazor Auto-Modus mit Wechsel zwischen Blazor Server und Blazor WebAssembly für Single-Page-Web-Apps.
Neu ist auch der Zuschnitt der Projektvorlagen (siehe ebenfalls Bild 1): Anstelle von Projektvorlagen für Blazor Server und für Blazor WebAssembly steht nun eine Projektvorlage Blazor Web App zur Verfügung, die sowohl Blazor SSR und Blazor Server als auch Blazor WebAssembly (mit ASP.NET Core Host) umfasst.Daneben deckt die eigenständige Vorlage Blazor WebAssembly Standalone App den Fall ab, dass Blazor WebAssembly nicht von einem ASP.NET-Core-fähigen Webserver, sondern einem beliebigen (statischen) Webserver heruntergeladen werden soll.Blazor SSR ist Thema dieses Beitrags und eines Folgeartikels in der nächsten dotnetpro-Ausgabe. Ein dritter und letzter Artikel wird sich mit dem Blazor Auto-Modus befassen.

Blazor SSR ist nicht Blazor Server

Der Begriff Static Server-Side-Rendering (Blazor SSR) kann verwirren. Erste Erfahrungen in der Praxis zeigen, dass zahlreiche Entwickler denken: „Das gibt es doch schon mit Blazor Server.“ Aber nein, Blazor Server und Blazor SSR sind nicht das Gleiche.Der schon seit .NET Core 3.1 verfügbare Blazor Server ­rendert zwar auf dem Server, es entsteht aber dennoch eine Single Page Application (SPA). Über eine WebSocket-Verbindung werden Unterschiede des Renderings zum vorherigen Rendering (im Programmiererlatein „Diff“ oder „Change Set“ genannt) zum Client gesendet und dort per JavaScript ausgetauscht. Der Benutzer bemerkt daher nicht, dass es ein Server-Rendering gab. Die Seite ist genauso interaktiv wie beim Einsatz von Blazor WebAssembly oder eines JavaScript-basierten Webfrontend-Frameworks wie Angular, React oder Vue.js.Mit dem neuen Blazor Static Server-Side-Rendering (Blazor SSR) bietet Microsoft im Rahmen von Blazor nun auch ­eine rein serverseitige, aus Client-Sicht statische HTML-Erzeugung zu einer Multi Page Application (MPA). Das Rendering-Ergebnis wird bei Blazor SSR in einem Rutsch über eine normale HTTP-Verbindung zum Client gesendet, und es werden immer ganze Seiten ausgetauscht, sodass der Benutzer ein typisches „Flackern“ der Darstellung beim Seitenwechsel wahrnimmt. Neue HTTP-Anfragen beim Server werden nur durch Hyperlinks oder Formulareinsendungen ausgelöst. .NET- oder JavaScript-Code im Browser ist bei Blazor SSR nicht notwendig.Die Architektur von Blazor SSR entspricht grundsätzlich dem, was bisher mit ASP.NET Core Model-View-Controller (MVC) und ASP.NET Core Razor Pages angeboten wurde. Allerdings kann Blazor SSR mit zusätzlichen Features punkten:
  • Blazor SSR bietet im Gegensatz zu MVC / Razor Pages ein echtes Komponentenmodell.
  • Die Razor-Template-Syntax in Blazor SSR hat mehr Funk­tionen.
  • Asynchron gerenderte Inhalte können in der HTTP-Antwort nachgesendet werden (Streaming).
  • Seitenteile können einzeln gesendet werden (Enhanced Navigation).
  • In einer statisch gerenderten Site können Entwicklerinnen und Entwickler SPA-Inseln (einzelne Seiten oder auch Seitenteile) einbetten, die mit Blazor Server, Blazor WebAssembly oder im Auto-Modus laufen.
Während Blazor SSR im Kern ohne JavaScript läuft, erfordern die letztgenannen drei Punkte die Einbindung der von Micro­soft gelieferten JavaScript-Datei blazor.web.js.

Blazor-Arten im Vergleich

Bild 2 zeigt die Architekturen ASP.NET Core MVC, ASP.NET Core Razor Pages, Blazor SSR ohne Streaming und Blazor SSR mit Streaming sowie Blazor Server und Blazor WebAssembly im Vergleich. JavaScript muss der Browser nur für Blazor Server, Blazor WebAssembly und Blazor SSR mit Strea­ming verarbeiten können, denn nur in diesen Fällen wird ­eine von Microsoft gelieferte JavaScript-Datei in den Web­browser geladen, nämlich:
Alle Blazor-Webanwendungsarten im Vergleich zu MVC und Razor Pages (Bild 2) © Autor
  • blazor.web.js (mit 180 KB) bei Projekten auf Basis der Vorlage Blazor Web App, beziehungsweise
  • blazor.webassembly.js, dotnet.js, dotnet.runtime.js und dotnet.native.js (mit in Summe rund 128 KB) bei Blazor WebAssembly Standalone App.
Bei Blazor SSR wird kein .NET-Code in den Browser geladen, daher ist auch kein WebAssembly im Browser notwendig.Die Tabelle 1 zeigt, welche Gemeinsamkeiten, aber auch welche technischen Funktionsunterschiede es zwischen MPAs mit Blazor SSR und Blazor-SPAs (mit Blazor Server beziehungsweise Blazor WebAssembly) gibt.

Tabelle 1: Funktionsvergleich für MPAs mit Blazor SSR und Blazor-SPAs (mit Blazor Server, Blazor WebAssembly)

Blazor SSR kann MVC und Razor Pages ersetzen

Nun stellt sich die Frage, wofür Microsoft nun Blazor SSR einführt, wenn doch die Architektur grundsätzlich dieselbe ist wie bei ASP.NET Core MVC und ASP.NET Core Razor Pages.Der Aufbau einer Komponente und auch die Razor-Syntax sind in Razor Components, die Blazor verwendet, nicht dieselben wie bei MVC und Razor Pages. Indem Microsoft nun das modernere Komponentenmodell aus Blazor auch komplett auf den Server bringt, hat der Entwickler zukünftig die Wahl, eine Razor-Komponente nicht nur in Blazor Server und Blazor WebAssembly, sondern auch komplett auf dem Server laufen zu lassen.Man kann sicherlich sagen: Das Grab für MVC und Razor Pages ist damit schon ausgehoben, auch wenn Microsoft wohl vorerst nicht verkünden wird, dass die beiden älteren Modelle beerdigt werden sollen.Blazor SSR lässt die Vorgänger MVC und Razor Pages zu Auslaufmodellen werden, denn die Razor Components von Blazor bieten eine einfachere Syntax, insbesondere für die Formulardatenbehandlung, Datenbindung und das Einbetten von Komponenten. Zudem bieten die Razor Components bei Blazor SSR auch Enhanced Naviga­tion und Streaming, was es beides bei MVC und Razor Pages nicht gibt.

Projektvorlage für Blazor SSR

Ein Blazor-SSR-Projekt erstellt man mit der seit .NET 8.0 neuen Projektvorlage Blazor Web App (siehe Bild 3). Für Blazor SSR wählt man bei Interaktivitätsmodus die Option None (siehe auch [1]). Die Auswahl bei Interactivity Location hat in diesem Fall keine Auswirkung. Die anderen Optionen zum Interaktivitätsmodus werden im dritten Teil dieser Artikelserie in der übernächsten dotnetpro-Ausgabe besprochen.
Einstellungen der Projektvorlage „Blazor Web App“ (Bild 3) © Autor
Das erstellte Projekt hat ähnliche Elemente wie die bisherigen Blazor-Projektvorlagen, aber im Detail sind einige Dinge anders:
  • App.razor ist die neue Startkomponente für die Anwendung. Seit Blazor 8.0 findet man hier die HTML-Grundstruk­tur, die vor Blazor 8.0 in der _Host.cshtml (bei Blazor Server) beziehungsweise index.html (bei Blazor WebAssembly) steckte. Die Datei enthält die Tags <HeadOutlet> als Platzhalter für die <Title>-Tags für den Seitentitel (siehe <Title> in Home.razor) und <Routes> als Platzhalter für den Seiteninhalt, den die Routingkomponente Routes.razor liefert.
  • Die Routingkomponente Routes.razor steuert eine Razor-Komponente anhand des URL an. Dafür sorgt das Element <Found> in der Datei mit Verweis auf die Rahmenlayout­seite, diese verweist auf die MainLayout.razor im /Components/Layout-Ordner. Wie bisher kann man weitere Elemen­te wie <Navigating> (für Ladeanzeigen) und <NotFound> (für nicht gefundene URLs – aber nur bei Blazor WebAssembly) festlegen beziehungsweise per Attribut AdditionalAssemblies beim <Router> auch zusätzliche bei der URL-Auswertung zu berücksichtigende Assemblies angeben, wenn man zusätzliche Seiten in Razor Class Libraries definiert hat.
  • Der Ordner /Components/Layout enthält die Rahmenlayoutseite MainLayout.razor mit dahinterliegender CSS-Datei (MainLayout.razor.css). Wenn Include Sample Pages nicht gewählt ist, findet man in MainLayout.razor keine Tags, sondern nur die Einbindung der jeweiligen Unter­seite mit @Body. Mit Beispielseiten findet man hier eine Rahmenseite mit Navigationsleiste. Die Navigationsleiste ist (wie bisher) ausgelagert in NavMenu.razor.
  • Ohne aktivierte Beispielseiten gibt es im Ordner /Components/Pages eine Startseite Home.razor mit statischem Begrüßungstext und eine Fehlerdarstellungsseite Error.razor. Mit der Option Include Sample Pages erhält man als zusätzliche Seite Weather.razor (vor Blazor 8.0: FeatchData.razor) mit der Anzeige zufälliger Wettervorhersage-Daten via Streaming Rendering (siehe unten).
  • In Program.cs findet man AddRazorComponents() und app.MapRazorComponents<App>().

Ein einfacher Zähler mit Blazor SSR

Einen Zähler zu implementieren ist ein Standardbeispiel für eine Webanwendung. Heutzutage würde man einen solchen Zähler in den meisten Fällen als SPA realisieren. Hier soll der Zähler mit Blazor SSR als MPA realisiert werden, um einige daraus resultierende Herausforderungen zu erläutern.Da sich bei SSR keine clientseitigen Ereignisse behandeln lassen, kann man einen Zähler mit Blazor SSR nicht wie in anderen Blazor-Arten mit dem @onclick-Handler realisieren:

Current count: @currentCount

+@increment  ... private void IncrementCount() {   currentCount += increment; }
Hier würde bei Blazor SSR nichts gezählt werden, da @onclick einfach ignoriert wird.Das Listing 1 zeigt eine Lösung mit Blazor SSR:
Listing 1: SSR-basierter Zähler mit Bindung an einen einfachen Datentyp
@page "/counterSSR/{initCount:int=0}/
  {increment:int=2}"
<PageTitle>Counter</PageTitle>
@code {
  [Parameter]
  public int? initCount { get; set; } = 0;
  [Parameter]
  public int? increment { get; set; } = 1;
}
<h2> Counter auf SSR-Weise (mit einfacher Property)
  </h2>
<p>Current count: @CurrentCount</p>
<EditForm Model="this" OnValidSubmit="IncrementCount" 
    FormName="CounterForm1">
  <input type="hidden" name="CurrentCount" 
    @bind="CurrentCount" />
    @* oder: <InputNumber @bind-Value="CurrentCount" 
    style="display:none" /> *@
  <button class="btn btn-primary">+@increment</button>
</EditForm>
@code {
  protected override void OnInitialized()
  {
    if (CurrentCount is null) 
    // nur beim ersten Aufruf der Seite
    {
      CurrentCount = initCount;
    }
  }
  [SupplyParameterFromForm]
  int? CurrentCount { get; set; } = null;
  private void IncrementCount()
  {
    CurrentCount += increment;
  }
} 
  • Man braucht ein Formular, wahlweise ein Standardformular (<form>) oder die in Blazor in allen Varianten verfügbare Wrapper-Komponente <EditForm>. Das Formular ist zwingend notwendig, um einen Postback zum Webserver auszulösen, mit dem der aktuelle Zählerwert übergeben wird.
  • Das Formular-Tag muss einen Namen besitzen, den man bei <form> mit @formname <form method=POST” @formname= ”Registration” Model= ”reg” @onsubmit=”HandleSub­mit”> und bei <EditForm> im Attribut FormName festlegt: <EditForm FormName=”Regis­tra­tion” Model=”reg” OnValid­Submit=”HandleSubmit”>
  • Ohne diese Namensangabe kommt es zum Laufzeitfehler. Der Formularname muss zudem eindeutig sein.
  • Der Zähler muss in einem Eingabefeld dargestellt werden. Das ist zwingend notwendig, da Blazor SSR im Gegensatz zu den SPA- und Hybrid-Varianten von Blazor zustandslos ist. Die Komponenten behalten ihren Zustand zwischen zwei Rundgängen nicht. Der Wert muss also immer wieder vom Browser zum Webserver zurück übertragen werden. Das Eingabefeld kann aber gegebenenfalls per CSS unsichtbar gemacht werden (style=”display:none”), oder man verwendet ein verstecktes Feld: <inputtype=”hidden”>
  • Zum Absenden des Formulars braucht man eine Schaltfläche. Jeder Klick auf die Schaltfläche führt zu einem POST-Request mit Rundgang vom Browser zum Webserver.
  • Da OnInitialized() bei jedem Rundgang immer wieder aufgerufen wird, ist darauf zu achten, dass die Initialisierung der Property CurrentCount nur beim ersten Aufruf erfolgt.
Die Implementierung des Zählers in Listing 1 zeigt als weiteren Aspekt die Handhabung von Parametern mit Properties, die mit [Parameter] annotiert sind. Diese Handhabung ist aber gleich wie bei Blazor-SPAs und Blazor Hybrid.Alternativ kann man ein Blazor-SSR-Formular mit <form> statt <EditForm> realisieren. Bei <form> hat man zwingend Method=”Post” (Groß- und Kleinschreibung nicht relevant) anzugeben. Bei <EditForm> ist dies optional. Bei <form> heißt die Eigenschaft zur Angabe der Methode, die den Klick auf dem Webserver behandelt, @onsubmit. Bei <EditForm> ist es OnValidSubmit.

POST" @onsubmit="IncrementCount" 
    @formname="CounterForm1">
  
  
  
    +@increment
 

Anti-Forgery-Token zum Schutz gegen Cross-Site Request Forgery

Für den Schutz gegen Angriffe nach dem Prinzip der Cross-Site Request Forgery (CSRF/XSRF) liefert Microsoft in ASP
.NET Core 8.0 eine neue Middleware, die Entwicklerinnen und Entwickler im Startcode einer ASP.NET-Core-Anwendung folgendermaßen aktivieren können:

builder.Services.AddAntiforgery(); 
Dieser Aufruf aktiviert zunächst nur in der Verarbeitungspipeline das Feature IAntiforgeryValidationFeature. Ein auf ASP.NET Core aufbauendes Webframework (zum Beispiel Blazor, Web API, MVC, Razor Pages) muss sodann ein AntiForgery-Token bei Formulareinsendungen mitsenden.In der Projektvorlage Blazor Web App findet man den Aufruf von app.UseAntiforgery() nach app.UseStaticFiles() und gegebenenfalls nach app.UseAuthentication() und app.Use­Authorization().Konsequenz ist, dass bei Blazor SSR alle Formulare ein Anti-Forgery-Token erzeugen müssen. <EditForm> erledigt das automatisch. Bei <form> muss man <AntiforgeryToken></AntiforgeryToken> explizit verwenden. Damit wird jeweils sichergestellt, dass ein eindeutiges Token pro Formularabsendung erzeugt und geprüft wird.Man kann die Anti-Forgery-Token auch pro Komponente deaktivieren:

@using Microsoft.AspNetCore.Antiforgery;
@attribute [RequireAntiforgeryToken(required: false)] 

Zustandsverwaltung per Cookies

Das erweiterte Zähler-Beispiel in Listing 2 zeigt die Reali­sierung des Zählers mit Zustandsverwaltung per Browser-Cookies und einem komplexen Objekt zur Datenbindung (CounterModel). Zugriff auf die Liste der Cookies erhält man über die Klasse HttpContext im Namensraum Microsoft.AspNetCore.Http (Assembly Microsoft.AspNetCore.Http.Ab­stractions). Eine Instanz davon wird bei Blazor SSR per kaskadierendem Parameter an alle Komponenten übergeben (nicht wie bei anderen Blazor-Arten per Dependency Injection!).
Listing 2: SSR-basierter Zähler mit Bindung an ein komplexes Objekt und Zustandsverwaltung per Cookie
@page "/counterSSRCookies/{initCount:int=0}/
  {increment:int=2}"
<PageTitle>Counter auf SSR-Weise (mit komplexer 
  Property und Cookies)</PageTitle>
@code {
  [Parameter]
  public int initCount { get; set; } = 0;
  [Parameter]
  public int increment { get; set; } = 1;
}
<h2>Counter auf SSR-Weise (mit komplexer Property und 
  Cookies)</h2>
<p>Current count: @counterModel.CurrentCount</p>
<EditForm method="POST" Model="counterModel" 
    OnValidSubmit="IncrementCount" 
    FormName="CounterForm">
  <InputNumber @bind-Value="counterModel.CurrentCount"
    style="display:none"/>
  <button class="btn btn-primary">+@increment</button>
</EditForm>
@code {
  [CascadingParameter]
  public HttpContext? HttpContext { get; set; }
 
  [SupplyParameterFromForm]
  CounterModel? counterModel { get; set; }
  class CounterModel
  {
    public int CurrentCount { get; set; } = 0;
    public int Increment { get; set; } = 1;
  }
  protected override async Task OnInitializedAsync()
  {
    if (counterModel is null) 
    // nur beim ersten Aufruf der Seite
    {
      counterModel = new();
      counterModel.CurrentCount = initCount;
      counterModel.Increment = increment;
    }
  }
  private void IncrementCount()
  {
    counterModel.CurrentCount = HttpContext.Request
      .Cookies["counter"] is null ? 0 : int.Parse(
      HttpContext.Request.Cookies["counter"]);
    counterModel.CurrentCount += increment;
    HttpContext.Response.Cookies.Append("counter", 
      counterModel.CurrentCount.ToString());
  }
} 
Eine Blazor-SSR-Komponente bezieht die aktive Instanz von HttpContext also so:

[CascadingParameter]
public HttpContext? HttpContext { get; set; } 
Man liest dann das Cookie per HttpContext.Request.Coo­kies[”Name”]. Über den Aufruf der Append()-Methode setzt man einen Cookie-Wert in der Antwort:

HttpContext.Response.Cookies.Append("Name", 
  "WertAlsZeichenkette")  

Ausblick

Die bisherigen Beispiele in diesem Beitrag zum neuen Static Server-Side-Rendering in Blazor kamen gut ohne JavaScript aus. In zweiten Teil in der nächsten Ausgabe wird Blazor SSR seine Vorteile gegenüber MVC und Razor Pages voll ausspielen, weil durch Einsatz der Bibliothek blazor.web.js das Streamen von gerendertem HTML und das Beibehalten der Browserposition auch nach einem Roundtrip möglich wird.

Fussnoten

  1. Microsoft Docs, Render-Modi in ASP.NET Core Blazor 8.0, http://www.dotnetpro.de/SL2404BlazorUnited1

Neueste Beiträge

Letzter Tag, große Themen: Git-Deep-Dive, AI-Diskussionen und fette Preise - DWX 2025
Am letzten Konferenztag wurde nicht etwa gemütlich ausgerollt – im Gegenteil. Die Agenda hatte es noch einmal in sich: ein technischer Deep-Dive, ein zukunftsweisendes Panel und ein paar ziemlich attraktive Gewinne. Hier kommt der Rückblick.
4 Minuten
4. Jul 2025
00:00
DWX Three takeaways, Ricardo Cachucho: AI is here to stay - Konferenz
Im Kurzinterview nennt Richardo Cachucho die drei wichtigsten Punkte seiner Keynote auf der DWX 25.
2. Jul 2025
DWX Daily: The Grand Opening - Konferenz
DWX, erster Konferenztag: Zum ersten Mal findet die DWX in Mannheim statt - in neuem Look and Feel. Das wurde gebührend gefeiert.
3 Minuten
1. Jul 2025
Miscellaneous

Das könnte Dich auch interessieren

Erstellung von ZUGFeRD 2.3 mit .NET C# - Rechnungserstellung
ZUGFeRD 2.3 konforme Rechnungen mit TX Text Control .NET Server für ASP.NET erstellen.
3 Minuten
9. Jan 2025
Einblicke der anderen Art: Neue .NET-Features entdecken - .NET
Entdecken Sie, wie man die neuesten .NET-Entwicklungen über GitHub verfolgt.
2 Minuten
11. Jun 2025
Wann wird die Unterstützung für das .NET Framework eingestellt? - .NET
Ein Blick auf die Lebenszyklus-Richtlinien von Microsoft zeigt, dass das .NET Framework weiterhin unterstützt wird, während sich die Community auf die neuesten Versionen konzentriert.
2 Minuten
28. Jan 2025
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige