SSR Renderung und Seiteneffekte ohne Ende

Aktuell stelle ich eine Blazor 8 Anwendung auf 9 um und erlebe einen seltsamen Effekt nach dem anderen. Vermutlich bin ich an die Grenzen von Static Rendering im Mix mit Server Interactive Rendering gegangen.
Begonnen haben meinen Probleme damit, das der HttpContext nicht mehr korrekt gefüllt wurde, Eigentlich ist seit Blazor 8 der HttpContext als CascadingValue implementiert.

So das man diesen in jeder Blazor Component mit folgenden Code nutzen kann

   1:    [CascadingParameter]
   2:    private HttpContext HttpContext { get; set; } = default!;

Die Zuweisung des HttpContext erfolgt mehr oder minder automatisch mit AddRazorComponents in programm.cs. Man kann sich das im Source auf Github auch ansehen. Dort findet sich die Zeile

services.TryAddCascadingValue(sp => 
sp.GetRequiredService<EndpointHtmlRenderer>().HttpContext);

Microsoft hat dokumentiert, das beim Übergang von SSR zu Interactive Server Rendering dieser den Context verlieren kann. Um der Sache auf den den Grund zu gehen, nutze ich nun das neue Objekt in .NET 9 RendererInfo. So kann man den Rendermode einer Komponente sich ausgeben lassen.

Startpunkt ist leeres Blazor Visual Studo Projekt mit per Page als Steuerung des Render Modes.

SSR1

In der app.razor wird der Rendermode angezeigt per RenderInfo

   1:  <body>
   2:      @RendererInfo.Name
   3:      <Routes />

Dazu noch in der Counter.razor

<h1>Counter   @RendererInfo.Name</h1>

Wir sehen nun den äußeren Rahmen als Static und den Counter selbst als Server gerendert.

ssr2

SSR nutzt für Navigation, also die Änderung der Url, nicht das Websocket Protokoll, sondern das HTTP Fetch Kommando.. Das kann man in den F12 Developer Tools des Browser auch gut nachverfolgen

ssr3

Also egal ob zur Home (static) oder Counter (Server) gewechselt wird, es erfolgt immer ein Fetch. Außer ich deaktiviere per Enhance oder data-enhance diese Funktion.

Nun wird ein Cascading Parameter eingeführt für Test Zwecke. Eine Blazor Page (Component + Route) ohne Rendermode wird in dieser Konfiguration statisch als HTML gerendet. Hier als home.razor

   1:  @page "/"
   2:  <PageTitle>Home</PageTitle>
   3:  <h1>Hello, world! @Hannes</h1>
   4:  @code
   5:  {
   6:      [CascadingParameter(Name = "Hannes")]
   7:      public int Hannes { get; set; }

Erwartungsgemäß wird der Inhalt der Variable angezeigt

ssr4

Um die Funktion der Counter.razor sicher zu stellen, muss diese Component auf rendermode InteractiveServer gesetzt werden.

Das Ergebnis überrascht. Kurz zeigt die Blazor Page den Wert des Cascading Parameters korrekt an um ihn dann auf 0 zu setzen.

ssr5

Wieder ist es ein Blazor Standard Verhalten, was hier zum tragen kommt. Erst wird die Seite statisch (Prerender) gerendert um dann in den Interactive Mode zu wechseln. Das hat unter anderem auch zur Folge das in einer Blazor Komponente OnInitialized zwei mal aufgerufen wird. Was unter meinen Blazor Schulungsteilnehmern jedes Mal für Erstaunen sorgt.

Wie löst man nun dieses Problem. Es muss früher der Render Mode gesetzt werden. Dafür kann man in die App.Razor folgenden Code einbauen.

   1:  @code {
   2:      [CascadingParameter]
   3:      private HttpContext HttpContext { get; set; } = default!;
   4:   
   5:      private IComponentRenderMode? PageRenderMode =>
   6:          HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
   7:  }

Die Sache mit dem HTTPContext, ist rein optional um zu prüfen ob dieser auch komplett zur Verfügung steht,

Im HTML Teil der app.razor muss nun die Variabale dem  Rendermode der Routing Component  zugewiesen werde. Selbiges am besten auch für Headoutlet.

   1:  <body>
   2:      @RendererInfo.Name
   3:     <Routes @rendermode=@PageRenderMode/>
   4:     @HttpContext.User.Identity.IsAuthenticated

Damit habe ich nun den Cascading Value ab Level App.razor inkl. aller Kind Komponenten sicher zur Verfügung für SSR und Server.

Kommentare sind geschlossen