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:

&lt;p&gt;Current count: <span class="hljs-keyword">@currentCount</span>&lt;/p&gt;
&lt;button <span class="hljs-keyword">class</span>=<span class="hljs-string">"btn btn-primary"</span> 
  <span class="hljs-keyword">@onclick</span>=<span class="hljs-string">"IncrementCount"</span>&gt;+<span class="hljs-keyword">@increment</span>&lt;/button&gt; 
...
<span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> 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
&lt;span class="hljs-meta"&gt;@page&lt;/span&gt; &lt;span class="hljs-string"&gt;"/counterSSR/{initCount:int=0}/&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-string"&gt;  {increment:int=2}"&lt;/span&gt;&lt;br/&gt;&amp;lt;PageTitle&amp;gt;Counter&amp;lt;/PageTitle&amp;gt;&lt;br/&gt;&lt;span class="hljs-meta"&gt;@code&lt;/span&gt; {&lt;br/&gt;  [Parameter]&lt;br/&gt;  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; int? initCount { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; } = &lt;span class="hljs-number"&gt;0&lt;/span&gt;;&lt;br/&gt;  [Parameter]&lt;br/&gt;  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; int? increment { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; } = &lt;span class="hljs-number"&gt;1&lt;/span&gt;;&lt;br/&gt;}&lt;br/&gt;&amp;lt;h2&amp;gt; Counter auf SSR-Weise (mit einfacher Property)&lt;br/&gt;  &amp;lt;/h2&amp;gt;&lt;br/&gt;&amp;lt;p&amp;gt;Current count: &lt;span class="hljs-meta"&gt;@CurrentCount&lt;/span&gt;&amp;lt;/p&amp;gt;&lt;br/&gt;&amp;lt;EditForm Model=&lt;span class="hljs-string"&gt;"this"&lt;/span&gt; OnValidSubmit=&lt;span class="hljs-string"&gt;"IncrementCount"&lt;/span&gt; &lt;br/&gt;    FormName=&lt;span class="hljs-string"&gt;"CounterForm1"&lt;/span&gt;&amp;gt;&lt;br/&gt;  &amp;lt;input type=&lt;span class="hljs-string"&gt;"hidden"&lt;/span&gt; name=&lt;span class="hljs-string"&gt;"CurrentCount"&lt;/span&gt; &lt;br/&gt;    &lt;span class="hljs-meta"&gt;@bind&lt;/span&gt;=&lt;span class="hljs-string"&gt;"CurrentCount"&lt;/span&gt; /&amp;gt;&lt;br/&gt;    @* oder: &amp;lt;InputNumber &lt;span class="hljs-meta"&gt;@bind&lt;/span&gt;-Value=&lt;span class="hljs-string"&gt;"CurrentCount"&lt;/span&gt; &lt;br/&gt;    style=&lt;span class="hljs-string"&gt;"display:none"&lt;/span&gt; /&amp;gt; *@&lt;br/&gt;  &amp;lt;button &lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt;="&lt;span class="hljs-title"&gt;btn&lt;/span&gt; &lt;span class="hljs-title"&gt;btn&lt;/span&gt;-&lt;span class="hljs-title"&gt;primary&lt;/span&gt;"&amp;gt;+&lt;span class="hljs-meta"&gt;@increment&lt;/span&gt;&amp;lt;&lt;span class="hljs-type"&gt;/button&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;&amp;lt;&lt;span class="hljs-type"&gt;/EditForm&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;&lt;span class="hljs-meta"&gt;@code&lt;/span&gt; &lt;/span&gt;{&lt;br/&gt;  &lt;span class="hljs-keyword"&gt;protected&lt;/span&gt; &lt;span class="hljs-keyword"&gt;override&lt;/span&gt; void OnInitialized()&lt;br/&gt;  {&lt;br/&gt;    &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (CurrentCount &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; &lt;span class="hljs-literal"&gt;null&lt;/span&gt;) &lt;br/&gt;    &lt;span class="hljs-comment"&gt;// nur beim ersten Aufruf der Seite&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;    {&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;      CurrentCount = initCount;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;    }&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;  }&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;  [SupplyParameterFromForm]&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;  int? CurrentCount { get; set; } = null;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;  private void IncrementCount()&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;  {&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;    CurrentCount += increment;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;  }&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;}&lt;/span&gt; 
  • 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.

&lt;form method=<span class="hljs-string">"<span class="hljs-keyword">POST</span>"</span> @onsubmit=<span class="hljs-string">"IncrementCount"</span> 
    @formname=<span class="hljs-string">"CounterForm1"</span>&gt;
  &lt;AntiforgeryToken&gt;&lt;/AntiforgeryToken&gt;
  &lt;input type=<span class="hljs-string">"hidden"</span> name=<span class="hljs-string">"CurrentCount"</span> 
    @bind=<span class="hljs-string">"CurrentCount"</span> /&gt;
  &lt;button type=<span class="hljs-string">"submit"</span> class=<span class="hljs-string">"btn btn-primary"</span>&gt;
    +@increment&lt;/button&gt;
&lt;/form&gt; 

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:

<span class="hljs-selector-tag">builder</span><span class="hljs-selector-class">.Services</span><span class="hljs-selector-class">.AddAntiforgery</span>(); 
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:

<span class="hljs-meta">@using</span> Microsoft.AspNetCore.Antiforgery;
<span class="hljs-meta">@attribute</span> [RequireAntiforgeryToken(<span class="hljs-string">required:</span> <span class="hljs-literal">false</span>)] 

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 &lt;span class="hljs-string"&gt;"/counterSSRCookies/{initCount:int=0}/&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-string"&gt;  {increment:int=2}"&lt;/span&gt;&lt;br/&gt;&amp;lt;PageTitle&amp;gt;Counter auf SSR-Weise (mit komplexer &lt;br/&gt;  Property und Cookies)&amp;lt;/PageTitle&amp;gt;&lt;br/&gt;@code {&lt;br/&gt;  [Parameter]&lt;br/&gt;  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;int&lt;/span&gt; initCount { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; } = &lt;span class="hljs-number"&gt;0&lt;/span&gt;;&lt;br/&gt;  [Parameter]&lt;br/&gt;  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;int&lt;/span&gt; increment { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; } = &lt;span class="hljs-number"&gt;1&lt;/span&gt;;&lt;br/&gt;}&lt;br/&gt;&amp;lt;h2&amp;gt;Counter auf SSR-Weise (mit komplexer Property und &lt;br/&gt;  Cookies)&amp;lt;/h2&amp;gt;&lt;br/&gt;&amp;lt;p&amp;gt;Current count: @counterModel.CurrentCount&amp;lt;/p&amp;gt;&lt;br/&gt;&amp;lt;EditForm method=&lt;span class="hljs-string"&gt;"POST"&lt;/span&gt; Model=&lt;span class="hljs-string"&gt;"counterModel"&lt;/span&gt; &lt;br/&gt;    OnValidSubmit=&lt;span class="hljs-string"&gt;"IncrementCount"&lt;/span&gt; &lt;br/&gt;    FormName=&lt;span class="hljs-string"&gt;"CounterForm"&lt;/span&gt;&amp;gt;&lt;br/&gt;  &amp;lt;InputNumber @bind-Value=&lt;span class="hljs-string"&gt;"counterModel.CurrentCount"&lt;/span&gt;&lt;br/&gt;    style=&lt;span class="hljs-string"&gt;"display:none"&lt;/span&gt;/&amp;gt;&lt;br/&gt;  &amp;lt;button &lt;span class="hljs-keyword"&gt;class&lt;/span&gt;=&lt;span class="hljs-string"&gt;"btn btn-primary"&lt;/span&gt;&amp;gt;+@increment&amp;lt;/button&amp;gt;&lt;br/&gt;&amp;lt;/EditForm&amp;gt;&lt;br/&gt;@code {&lt;br/&gt;  [CascadingParameter]&lt;br/&gt;  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; HttpContext? HttpContext { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; }&lt;br/&gt; &lt;br/&gt;  [SupplyParameterFromForm]&lt;br/&gt;  CounterModel? counterModel { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; }&lt;br/&gt;  &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title"&gt;CounterModel&lt;/span&gt;&lt;br/&gt;  {&lt;br/&gt;    &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;int&lt;/span&gt; CurrentCount { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; } = &lt;span class="hljs-number"&gt;0&lt;/span&gt;;&lt;br/&gt;    &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;int&lt;/span&gt; Increment { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; } = &lt;span class="hljs-number"&gt;1&lt;/span&gt;;&lt;br/&gt;  }&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;protected&lt;/span&gt; &lt;span class="hljs-keyword"&gt;override&lt;/span&gt; &lt;span class="hljs-keyword"&gt;async&lt;/span&gt; Task &lt;span class="hljs-title"&gt;OnInitializedAsync&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;  &lt;/span&gt;{&lt;br/&gt;    &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (counterModel &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; &lt;span class="hljs-literal"&gt;null&lt;/span&gt;) &lt;br/&gt;    &lt;span class="hljs-comment"&gt;// nur beim ersten Aufruf der Seite&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;    {&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;      counterModel = new();&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;      counterModel.CurrentCount = initCount;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;      counterModel.Increment = increment;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;    }&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;  }&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;  private void IncrementCount()&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;  {&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;    counterModel.CurrentCount = HttpContext.Request&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;      .Cookies["counter"] is null ? 0 : int.Parse(&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;      HttpContext.Request.Cookies["counter"]);&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;    counterModel.CurrentCount += increment;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;    HttpContext.Response.Cookies.Append("counter", &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;      counterModel.CurrentCount.ToString());&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;  }&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;}&lt;/span&gt; 
Eine Blazor-SSR-Komponente bezieht die aktive Instanz von HttpContext also so:

[CascadingParameter]
<span class="hljs-keyword">public</span> HttpContext? HttpContext { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } 
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<span class="hljs-selector-class">.Response</span><span class="hljs-selector-class">.Cookies</span><span class="hljs-selector-class">.Append</span>(<span class="hljs-string">"Name"</span>, 
  <span class="hljs-string">"WertAlsZeichenkette"</span>)  

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

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Evolutionäres Prototyping von Business-Apps - Low Code/No Code und KI mit Power Apps
Microsoft baut Power Apps zunehmend mit Features aus, um die Low-Code-/No-Code-Welt mit der KI und der professionellen Programmierung zu verbinden.
19 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige