Delen via


statusbeheer van ASP.NET Core Blazor

Notitie

Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Waarschuwing

Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Belangrijk

Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.

Zie de .NET 9-versie van dit artikelvoor de huidige release.

In dit artikel worden algemene benaderingen beschreven voor het onderhouden van de gegevens (status) van een gebruiker terwijl ze een app en in browsersessies gebruiken.

Notitie

De codevoorbeelden in dit artikel gebruiken nullable reference types (NRT's) en .NET compiler null-state statische analyse, die worden ondersteund in ASP.NET Core in .NET 6 of hoger. Wanneer u zich richt op ASP.NET Core 5.0 of eerder, verwijdert u de null-typeaanduiding (?) uit typen in de voorbeelden van het artikel.

Gebruikersstatus behouden

Blazor aan de serverzijde is een stateful app-framework. Meestal onderhoudt de app een verbinding met de server. De status van de gebruiker wordt bewaard in het geheugen van de server in een circuit.

Voorbeelden van gebruikersstatus in een circuit zijn:

  • De hiërarchie van componenten en hun meest recente weergave-output in de weergegeven gebruikersinterface.
  • De waarden van velden en eigenschappen in onderdeelexemplaren.
  • Gegevens die zijn opgeslagen in afhankelijkheidsinjectie (DI) service-exemplaren die binnen het bereik van het circuit vallen.

De gebruikersstatus kan ook worden gevonden in JavaScript-variabelen in de geheugenset van de browser via JavaScript-interop aanroepen.

Als een gebruiker een tijdelijk netwerkverbindingsverlies ondervindt, probeert Blazor de gebruiker opnieuw te verbinden met het oorspronkelijke circuit met de oorspronkelijke status. Het opnieuw verbinden van een gebruiker met het oorspronkelijke circuit in het geheugen van de server is echter niet altijd mogelijk:

  • De server kan een niet-verbonden circuit niet voor altijd behouden. De server moet een niet-verbonden circuit vrijgeven na een time-out of wanneer de server onder geheugendruk staat.
  • In implementatieomgevingen met meerdere servers met gelijke taakverdeling kunnen afzonderlijke servers mislukken of automatisch worden verwijderd wanneer ze niet langer nodig zijn om het totale aantal aanvragen af te handelen. De oorspronkelijke serververwerkingsaanvragen voor een gebruiker zijn mogelijk niet beschikbaar wanneer de gebruiker opnieuw verbinding probeert te maken.
  • De gebruiker kan de browser sluiten en opnieuw openen of de pagina opnieuw laden, waardoor de status in het geheugen van de browser wordt verwijderd. De waarden voor JavaScript-variabelen die zijn ingesteld via JavaScript-interop-aanroepen, gaan bijvoorbeeld verloren.

Wanneer een gebruiker niet opnieuw verbinding kan maken met het oorspronkelijke circuit, ontvangt de gebruiker een nieuw circuit met een lege status. Dit komt overeen met het sluiten en opnieuw openen van een bureaublad-app.

Status behouden tussen circuits

Over het algemeen houd de status bij over circuits heen waar gebruikers actief gegevens aanmaken en niet slechts gegevens lezen die al bestaan.

Als u de status tussen circuits wilt behouden, moet de app de gegevens bewaren naar een andere opslaglocatie dan het geheugen van de server. Statuspersistentie is niet automatisch. U moet stappen ondernemen bij het ontwikkelen van de app om gegevenspersistentie met behoud van staat te implementeren.

Gegevenspersistentie is doorgaans alleen vereist voor hoogwaardige status die gebruikers moeite hebben gedaan om te creëren. In de volgende voorbeelden bespaart aanhoudende status tijd of helpt in commerciële activiteiten.

  • Webformulieren met meerdere stappen: het is tijdrovend voor een gebruiker om gegevens opnieuw in te voeren voor verschillende voltooide stappen van een webformulier met meerdere stappen als de status verloren gaat. Een gebruiker verliest de status in dit scenario als deze weg van het formulier navigeert en later terugkeert.
  • Winkelwagens: Elk commercieel belangrijk onderdeel van een app die potentiële omzet vertegenwoordigt, kan worden onderhouden. Een gebruiker die zijn status verliest, en dus zijn winkelwagen, kan minder producten of services kopen wanneer ze later terugkeren naar de site.

Een app kan alleen app-statusbehouden. UI's kunnen niet worden behouden, zoals componenten en hun renderbomen. Onderdelen en renderstructuren zijn over het algemeen niet serialiseerbaar. Om de UI-status te behouden, zoals de uitgebreide knooppunten van een boomstructuurweergave, moet de applicatie aangepaste code gebruiken om het gedrag van de UI-status te modelleren als serialiseerbare applicatiestatus.

Waar moet ik de status behouden?

Er bestaan algemene locaties voor persistente status:

Opslag aan serverzijde

Voor permanente gegevenspersistentie die meerdere gebruikers en apparaten omvat, kan de app opslag aan de serverzijde gebruiken. Opties zijn onder andere:

  • Blob-opslag
  • Sleutel-waardeopslag
  • Relationele database
  • Tabelopslag

Nadat de gegevens zijn opgeslagen, blijft de status van de gebruiker behouden en beschikbaar in elk nieuw circuit.

Zie het volgende voor meer informatie over opties voor Azure-gegevensopslag:

URL

Voor tijdelijke gegevens die de navigatiestatus vertegenwoordigen, modelleert u de gegevens als onderdeel van de URL. Voorbeelden van gebruikersstatus die in de URL zijn gemodelleerd, zijn:

  • De id van een bekeken entiteit.
  • Het huidige paginanummer in een gepaginad raster.

De inhoud van de adresbalk van de browser blijft behouden:

  • Als de gebruiker de pagina handmatig opnieuw laadt.
  • Als de webserver niet meer beschikbaar is en de gebruiker wordt gedwongen de pagina opnieuw te laden om verbinding te maken met een andere server.

Zie ASP.NET Core Blazor routering en navigatievoor informatie over het definiëren van URL-patronen met de @page-instructie.

Browseropslag

Voor tijdelijke gegevens die de gebruiker actief maakt, is een veelgebruikte opslaglocatie de localStorage en sessionStorage verzamelingen van de browser:

  • localStorage is beperkt tot het exemplaar van de browser. Als de gebruiker de pagina opnieuw laadt of de browser sluit en opnieuw opent, blijft de status behouden. Als de gebruiker meerdere browsertabbladen opent, wordt de status gedeeld op de tabbladen. Gegevens blijven bewaard in localStorage totdat ze expliciet zijn gewist. De localStorage gegevens voor een document dat is geladen in een sessie 'privé browsen' of 'incognito' wordt gewist wanneer het laatste tabblad 'privé' wordt gesloten.
  • sessionStorage is gericht op het browsertabblad. Als de gebruiker het tabblad opnieuw laadt, blijft de status behouden. Als de gebruiker het tabblad of de browser sluit, gaat de status verloren. Als de gebruiker meerdere browsertabbladen opent, heeft elk tabblad een eigen onafhankelijke versie van de gegevens.

Over het algemeen is sessionStorage veiliger te gebruiken. sessionStorage het risico voorkomt dat een gebruiker meerdere tabbladen opent en het volgende tegenkomt:

  • Fouten in statusopslag op tabbladen.
  • Verwarrend gedrag wanneer een tabblad de status van andere tabbladen overschrijft.

localStorage is de betere keuze als de app de status moet behouden bij het sluiten en opnieuw openen van de browser.

Opmerkingen bij het gebruik van browseropslag:

  • Net als bij het gebruik van een database aan de serverzijde zijn het laden en opslaan van gegevens asynchroon.
  • De aangevraagde pagina bestaat niet in de browser tijdens het prerendering, dus lokale opslag is niet beschikbaar tijdens het prerendering.
  • De opslag van een paar kilobytes aan gegevens is redelijk om aan te houden voor server-side Blazor apps. Na een paar kilobytes moet u rekening houden met de gevolgen voor de prestaties omdat de gegevens worden geladen en opgeslagen in het netwerk.
  • Gebruikers kunnen de gegevens bekijken of manipuleren. ASP.NET Core Data Protection kan het risico beperken. ASP.NET Core Protected Browser Storage maakt bijvoorbeeld gebruik van ASP.NET Core Data Protection.

NuGet-pakketten van derden bieden API's voor het werken met localStorage en sessionStorage. Het is de moeite waard om een pakket te kiezen dat transparant gebruikmaakt van ASP.NET Core Data Protection. Data Protection versleutelt opgeslagen gegevens en vermindert het potentiële risico op manipulatie met opgeslagen gegevens. Als JSON-geserialiseerde gegevens worden opgeslagen in tekst zonder opmaak, kunnen gebruikers de gegevens zien met hulpprogramma's voor browserontwikkelaars en de opgeslagen gegevens ook wijzigen. Het beveiligen van triviale gegevens is geen probleem. Het lezen of wijzigen van de opgeslagen kleur van een UI-element is bijvoorbeeld geen aanzienlijk beveiligingsrisico voor de gebruiker of de organisatie. Voorkom dat gebruikers gevoelige gegevens kunnen inspecteren of knoeien.

ASP.NET Core Beschermde Browseropslag

ASP.NET Core Protected Browser Storage maakt gebruik van ASP.NET Core Data Protection- voor localStorage en sessionStorage.

Notitie

Beveiligde browseropslag is afhankelijk van ASP.NET Core Data Protection en wordt alleen ondersteund voor Blazor-apps aan de serverzijde.

Waarschuwing

Microsoft.AspNetCore.ProtectedBrowserStorage is een niet-ondersteund, experimenteel pakket dat niet is bedoeld voor productiegebruik.

Het pakket is alleen beschikbaar voor gebruik in ASP.NET Core 3.1-apps.

Configuratie

  1. Voeg een pakketreferentie toe aan Microsoft.AspNetCore.ProtectedBrowserStorage.

    Notitie

    Raadpleeg de artikelen onder Pakketten installeren en beheren bij Pakkettenconsumptiewerkwijze (NuGet-documentatie)voor hulp bij het toevoegen van pakketten aan .NET-apps. Bevestig de juiste pakketversies op NuGet.org.

  2. Voeg in het bestand _Host.cshtml het volgende script toe in de afsluitende </body>-tag:

    <script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>
    
  3. Roep in Startup.ConfigureServicesAddProtectedBrowserStorage aan om localStorage en sessionStorage services toe te voegen aan de serviceverzameling:

    services.AddProtectedBrowserStorage();
    

Gegevens in een onderdeel opslaan en laden

Gebruik de @inject instructie om een exemplaar van een van de volgende items te injecteren in een van de volgende onderdelen waarvoor het laden of opslaan van gegevens in de browseropslag is vereist:

  • ProtectedLocalStorage
  • ProtectedSessionStorage

De keuze is afhankelijk van de opslaglocatie van de browser die u wilt gebruiken. In het volgende voorbeeld wordt sessionStorage gebruikt:

@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore

De @using instructie kan in het _Imports.razor-bestand van de app worden geplaatst in plaats van in het onderdeel. Gebruik van het _Imports.razor-bestand maakt de naamruimte beschikbaar voor grotere segmenten van de app of de hele app.

Als u de currentCount waarde in het Counter onderdeel van een app wilt behouden op basis van de Blazor projectsjabloon, wijzigt u de IncrementCount methode om ProtectedSessionStore.SetAsyncte gebruiken:

private async Task IncrementCount()
{
    currentCount++;
    await ProtectedSessionStore.SetAsync("count", currentCount);
}

In grotere, realistischere apps is de opslag van afzonderlijke velden een onwaarschijnlijk scenario. Apps slaan waarschijnlijk hele modelobjecten op die complexe status bevatten. ProtectedSessionStore serialiseert en deserialiseert automatisch JSON-gegevens om complexe statusobjecten op te slaan.

In het voorgaande codevoorbeeld worden de currentCount gegevens opgeslagen als sessionStorage['count'] in de browser van de gebruiker. De gegevens worden niet opgeslagen in tekst zonder opmaak, maar worden beveiligd met ASP.NET Core Data Protection. De versleutelde gegevens kunnen worden gecontroleerd als sessionStorage['count'] wordt geëvalueerd in de ontwikkelaarsconsole van de browser.

Als u de currentCount gegevens wilt herstellen als de gebruiker later terugkeert naar het Counter onderdeel, inclusief als de gebruiker zich op een nieuw circuit bevindt, gebruikt u ProtectedSessionStore.GetAsync:

protected override async Task OnInitializedAsync()
{
    var result = await ProtectedSessionStore.GetAsync<int>("count");
    currentCount = result.Success ? result.Value : 0;
}
protected override async Task OnInitializedAsync()
{
    currentCount = await ProtectedSessionStore.GetAsync<int>("count");
}

Als de parameters van het onderdeel de navigatiestatus bevatten, roept u ProtectedSessionStore.GetAsync aan en wijst u een niet-null resultaat toe aan OnParametersSetAsync, niet OnInitializedAsync. OnInitializedAsync wordt slechts eenmaal aangeroepen wanneer het onderdeel voor het eerst wordt geïnstantieerd. OnInitializedAsync wordt later niet opnieuw aangeroepen als de gebruiker naar een andere URL navigeert terwijl deze op dezelfde pagina blijft. Zie ASP.NET Core Razor levenscyclus van onderdelen voor meer informatie.

Waarschuwing

De voorbeelden in deze sectie werken alleen als de server geen prerendering heeft ingeschakeld. Als prerendering is ingeschakeld, wordt er een fout gegenereerd waarin wordt uitgelegd dat JavaScript-interopaanroepen niet kunnen worden uitgegeven omdat het onderdeel wordt voorbereid.

Schakel prerendering uit of voeg extra code toe om te werken met prerendering. Zie de sectie Handle prerendering voor meer informatie over het schrijven van code die werkt met prerendering.

De laadstatus afhandelen

Omdat browseropslag asynchroon wordt geopend via een netwerkverbinding, is er altijd een periode voordat de gegevens worden geladen en beschikbaar zijn voor een onderdeel. Voor de beste resultaten wordt een bericht weergegeven terwijl het laden wordt uitgevoerd in plaats van lege of standaardgegevens weer te geven.

Een methode is om bij te houden of de gegevens nullzijn, wat betekent dat de gegevens nog steeds worden geladen. In het standaardonderdeel Counter wordt het aantal in een intbewaard. currentCount nullable maken door een vraagteken (?) toe te voegen aan het type (int):

private int? currentCount;

In plaats van het aantal en de Increment-knop onvoorwaardelijk weer te geven, worden deze elementen alleen getoond als de gegevens zijn geladen door HasValuete controleren.

@if (currentCount.HasValue)
{
    <p>Current count: <strong>@currentCount</strong></p>
    <button @onclick="IncrementCount">Increment</button>
}
else
{
    <p>Loading...</p>
}

Prerendering afhandelen

Tijdens het voorrenderen:

  • Er bestaat geen interactieve verbinding met de browser van de gebruiker.
  • De browser heeft nog geen pagina waarin JavaScript-code kan worden uitgevoerd.

localStorage of sessionStorage zijn niet beschikbaar tijdens het prerendering. Als het onderdeel probeert te communiceren met opslag, wordt er een fout gegenereerd waarin wordt uitgelegd dat JavaScript-interoperabiliteitsaanroepen niet kunnen worden uitgegeven omdat het onderdeel wordt voorbereid.

Een manier om de fout op te lossen, is door prerendering uit te schakelen. Dit is meestal de beste keuze als de app intensief gebruik maakt van op browsers gebaseerde opslag. Prerendering voegt complexiteit toe en profiteert niet van de app, omdat de app pas nuttige inhoud kan prerenren als localStorage of sessionStorage beschikbaar zijn.

Als u prerendering wilt uitschakelen, geeft u de weergavemodus aan met de parameter prerender ingesteld op false op het hoogste niveau in de componenthiërarchie van de app die geen hoofdonderdeel is.

Notitie

Het interactief maken van een hoofdonderdeel, zoals het App-onderdeel, wordt niet ondersteund. Daarom kan prerendering niet rechtstreeks worden uitgeschakeld door het App onderdeel.

Voor apps op basis van de Blazor Web App projectsjabloon wordt het prerendering meestal uitgeschakeld wanneer het Routes onderdeel wordt gebruikt in het App-onderdeel (Components/App.razor):

<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />

Schakel ook het prerendering uit voor het HeadOutlet-onderdeel:

<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />

Zie ASP.NET Core Blazor rendermodivoor meer informatie.

Als u het prerendering wilt uitschakelen, opent u het _Host.cshtml bestand en wijzigt u het kenmerk render-mode van de Component Tag Helper in Server:

<component type="typeof(App)" render-mode="Server" />

Wanneer prerendering is uitgeschakeld, wordt prerendering van <head> inhoud uitgeschakeld.

Prerendering kan nuttig zijn voor andere pagina's die geen localStorage of sessionStoragegebruiken. Als u het prerendering wilt behouden, moet u de laadbewerking uitstellen totdat de browser is verbonden met het circuit. Hier volgt een voorbeeld voor het opslaan van een tellerwaarde:

@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedLocalStorage ProtectedLocalStore

@if (isConnected)
{
    <p>Current count: <strong>@currentCount</strong></p>
    <button @onclick="IncrementCount">Increment</button>
}
else
{
    <p>Loading...</p>
}

@code {
    private int currentCount;
    private bool isConnected;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            isConnected = true;
            await LoadStateAsync();
            StateHasChanged();
        }
    }

    private async Task LoadStateAsync()
    {
        var result = await ProtectedLocalStore.GetAsync<int>("count");
        currentCount = result.Success ? result.Value : 0;
    }

    private async Task IncrementCount()
    {
        currentCount++;
        await ProtectedLocalStore.SetAsync("count", currentCount);
    }
}
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedLocalStorage ProtectedLocalStore

@if (isConnected)
{
    <p>Current count: <strong>@currentCount</strong></p>
    <button @onclick="IncrementCount">Increment</button>
}
else
{
    <p>Loading...</p>
}

@code {
    private int currentCount = 0;
    private bool isConnected = false;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            isConnected = true;
            await LoadStateAsync();
            StateHasChanged();
        }
    }

    private async Task LoadStateAsync()
    {
        currentCount = await ProtectedLocalStore.GetAsync<int>("count");
    }

    private async Task IncrementCount()
    {
        currentCount++;
        await ProtectedLocalStore.SetAsync("count", currentCount);
    }
}

Splits toestandbeheer uit naar een gemeenschappelijke provider

Als veel onderdelen afhankelijk zijn van opslag op basis van een browser, maakt het implementeren van statusprovidercode vaak codeduplicatie. Een optie om codeduplicatie te vermijden is om een oudercomponent te maken dat de logica van de statusprovider inkapselt. Deelcomponenten kunnen werken met persistente gegevens, zonder rekening te houden met het mechanisme voor statuspersistentie.

In het volgende voorbeeld van een CounterStateProvider-onderdeel worden tellergegevens opgeslagen in sessionStorageen worden de laadfase verwerkt door de onderliggende inhoud pas weer te geven nadat het laden van de status is voltooid.

Het CounterStateProvider onderdeel heeft betrekking op het prerenderen door de status pas te laden nadat het onderdeel wordt weergegeven in de OnAfterRenderAsync levenscyclusmethode, die niet wordt uitgevoerd tijdens het genereren.

De methode in deze sectie is niet geschikt voor het activeren van rerendering van meerdere geabonneerde onderdelen op dezelfde pagina. Als een geabonneerd onderdeel de staat verandert, wordt het opnieuw gerenderd en kan het de bijgewerkte staat weergeven. Echter, een ander onderdeel op dezelfde pagina dat die staat toont, geeft verouderde gegevens weer totdat het zichzelf opnieuw renderen. Daarom is de methode die in deze sectie wordt beschreven, het meest geschikt voor het gebruik van de status in één onderdeel op de pagina.

CounterStateProvider.razor:

@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore

@if (isLoaded)
{
    <CascadingValue Value="this">
        @ChildContent
    </CascadingValue>
}
else
{
    <p>Loading...</p>
}

@code {
    private bool isLoaded;

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    public int CurrentCount { get; set; }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            isLoaded = true;
            await LoadStateAsync();
            StateHasChanged();
        }
    }

    private async Task LoadStateAsync()
    {
        var result = await ProtectedSessionStore.GetAsync<int>("count");
        CurrentCount = result.Success ? result.Value : 0;
        isLoaded = true;
    }

    public async Task IncrementCount()
    {
        CurrentCount++;
        await ProtectedSessionStore.SetAsync("count", CurrentCount);
    }
}
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore

@if (isLoaded)
{
    <CascadingValue Value="this">
        @ChildContent
    </CascadingValue>
}
else
{
    <p>Loading...</p>
}

@code {
    private bool isLoaded;

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public int CurrentCount { get; set; }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            isLoaded = true;
            await LoadStateAsync();
            StateHasChanged();
        }
    }

    private async Task LoadStateAsync()
    {
        CurrentCount = await ProtectedSessionStore.GetAsync<int>("count");
        isLoaded = true;
    }

    public async Task IncrementCount()
    {
        CurrentCount++;
        await ProtectedSessionStore.SetAsync("count", CurrentCount);
    }
}

Notitie

Zie ASP.NET Core Razor-onderdelenvoor meer informatie over RenderFragment.

Als u de status toegankelijk wilt maken voor alle onderdelen in een app, verpakt u het CounterStateProvider onderdeel rond de Router (<Router>...</Router>) in het Routes onderdeel met globale interactieve serverzijdeweergave (interactieve SSR).

In het onderdeel App (Components/App.razor):

<Routes @rendermode="InteractiveServer" />

In het onderdeel Routes (Components/Routes.razor):

Als u het CounterStateProvider-onderdeel wilt gebruiken, verpakt u een exemplaar van het onderdeel rond elk ander onderdeel waarvoor toegang tot de tellerstatus is vereist. Als u de status toegankelijk wilt maken voor alle onderdelen in een app, verpakt u het CounterStateProvider onderdeel rond de Router in het App-onderdeel (App.razor):

<CounterStateProvider>
    <Router ...>
        ...
    </Router>
</CounterStateProvider>

Notitie

Met de release van ASP.NET Core 5.0.1 en voor eventuele extra 5.x-releases bevat het Router onderdeel de PreferExactMatches parameter die is ingesteld op @true. Zie Migreren van ASP.NET Core 3.1 naar 5.0voor meer informatie.

Verpakte onderdelen ontvangen en kunnen de persistente tellerstatus wijzigen. Met het volgende Counter onderdeel wordt het patroon geïmplementeerd:

@page "/counter"

<p>Current count: <strong>@CounterStateProvider?.CurrentCount</strong></p>
<button @onclick="IncrementCount">Increment</button>

@code {
    [CascadingParameter]
    private CounterStateProvider? CounterStateProvider { get; set; }

    private async Task IncrementCount()
    {
        if (CounterStateProvider is not null)
        {
            await CounterStateProvider.IncrementCount();
        }
    }
}

Het voorgaande onderdeel is niet vereist om te communiceren met ProtectedBrowserStorage, noch wordt er een 'laadfase' uitgevoerd.

Over het algemeen wordt het patroon van de -toestandprovider bij de bovenliggende component aanbevolen:

  • Om de status over veel componenten te gebruiken.
  • Als er slechts één statusobject op het hoogste niveau is om vast te houden.

Als u veel verschillende statusobjecten wilt behouden en verschillende subsets van objecten op verschillende plaatsen wilt gebruiken, is het beter om permanente status wereldwijd te voorkomen.

De gebruikersstatus die is gemaakt in een Blazor WebAssembly-app wordt bewaard in het geheugen van de browser.

Voorbeelden van gebruikersstatus in het browsergeheugen zijn:

  • De hiërarchie van componentinstanties en hun meest recente render-uitvoer in de weergegeven gebruikersinterface.
  • De waarden van velden en eigenschappen in onderdeelexemplaren.
  • Gegevens die zijn opgeslagen in service-instanties van de dependency injection (DI).
  • Waarden die zijn ingesteld via JavaScript-interop oproepen.

Wanneer een gebruiker de browser sluit en opnieuw opent of de pagina opnieuw laadt, gaat de gebruikersstatus in het geheugen van de browser verloren.

Notitie

Protected Browser Storage (Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage naamruimte) is afhankelijk van ASP.NET Core Data Protection en wordt alleen ondersteund voor Blazor-apps aan de serverzijde.

Status behouden tussen browsersessies

Over het algemeen onderhoudt u de status in browsersessies waarin gebruikers actief gegevens maken, niet alleen gegevens lezen die al bestaan.

Als u de status tussen browsersessies wilt behouden, moet de app de gegevens bewaren naar een andere opslaglocatie dan het geheugen van de browser. Statuspersistentie is niet automatisch. u moet stappen ondernemen bij het ontwikkelen van de app om opslag van gegevens met behoud van status te implementeren.

Gegevenspersistentie is doorgaans alleen vereist voor de belangrijke of waardevolle status die gebruikers met moeite hebben gecreëerd. In de volgende voorbeelden bespaart een blijvende status tijd of helpt bij commerciële activiteiten.

  • Webformulieren met meerdere stappen: Het is tijdrovend voor een gebruiker om gegevens opnieuw in te voeren voor verschillende voltooide stappen wanneer hun status verloren is gegaan. Een gebruiker verliest de status in dit scenario als deze weg van het formulier navigeert en later terugkeert.
  • Winkelwagens: Elk commercieel belangrijk onderdeel van een app die potentiële omzet vertegenwoordigt, kan worden onderhouden. Een gebruiker die zijn status verliest, en dus zijn winkelwagen, kan minder producten of services kopen wanneer ze later terugkeren naar de site.

Een app kan alleen app-statusbehouden. UIS's kunnen niet worden behouden, zoals onderdeelexemplaren en hun renderstructuren. Onderdelen en renderstructuren zijn over het algemeen niet serialiseerbaar. Als u de UI-status wilt behouden, zoals uitgebreide knooppunten in een boomweergave-besturingselement, moet de app aangepaste code gebruiken om het gedrag van de UI-status te modelleren als een app-status die kan worden geserialiseerd.

Waar moet ik de status behouden?

Er bestaan algemene locaties voor persistente status:

Opslag aan serverzijde

Voor permanente gegevenspersistentie die meerdere gebruikers en apparaten omvat, kan de app gebruikmaken van onafhankelijke opslag aan de serverzijde die toegankelijk is via een web-API. Opties zijn onder andere:

  • Blob-opslag
  • Sleutel-waardeopslag
  • Relationele database
  • Tabelopslag

Nadat de gegevens zijn opgeslagen, blijft de status van de gebruiker behouden en beschikbaar in elke nieuwe browsersessie.

Omdat Blazor WebAssembly apps volledig worden uitgevoerd in de browser van de gebruiker, vereisen ze aanvullende maatregelen voor toegang tot beveiligde externe systemen, zoals opslagservices en databases. Blazor WebAssembly apps worden op dezelfde manier beveiligd als toepassingen met één pagina (SPA's). Normaal gesproken verifieert een app een gebruiker via OAuth/OpenID Connect (OIDC) en communiceert deze vervolgens met opslagservices en databases via web-API-aanroepen naar een app aan de serverzijde. De app aan de serverzijde bemiddelt de overdracht van gegevens tussen de Blazor WebAssembly-app en de opslagservice of -database. De Blazor WebAssembly-app onderhoudt een tijdelijke verbinding met de app aan de serverzijde, terwijl de app aan de serverzijde een permanente verbinding met de opslag heeft.

Zie de volgende bronnen voor meer informatie:

Zie het volgende voor meer informatie over opties voor Azure-gegevensopslag:

URL

Voor tijdelijke gegevens die de navigatiestatus vertegenwoordigen, modelleert u de gegevens als onderdeel van de URL. Voorbeelden van gebruikersstatus die in de URL zijn gemodelleerd, zijn:

  • De id van een bekeken entiteit.
  • Het huidige paginanummer in een gepaginad raster.

De inhoud van de adresbalk van de browser blijft behouden als de gebruiker de pagina handmatig opnieuw laadt.

Zie ASP.NET Core Blazor routering en navigatievoor informatie over het definiëren van URL-patronen met de @page-instructie.

Browseropslag

Voor tijdelijke gegevens die de gebruiker actief maakt, is een veelgebruikte opslaglocatie de localStorage en sessionStorage verzamelingen van de browser:

  • localStorage is beperkt tot de instantie van de browser. Als de gebruiker de pagina opnieuw laadt of de browser sluit en opnieuw opent, blijft de status behouden. Als de gebruiker meerdere browsertabbladen opent, wordt de status gedeeld op de tabbladen. Gegevens blijven bewaard in localStorage totdat ze expliciet zijn gewist. De localStorage gegevens voor een document dat is geladen in een sessie 'privé browsen' of 'incognito' wordt gewist wanneer het laatste tabblad 'privé' wordt gesloten.
  • sessionStorage is gericht op het browsertabblad. Als de gebruiker het tabblad opnieuw laadt, blijft de status behouden. Als de gebruiker het tabblad of de browser sluit, gaat de status verloren. Als de gebruiker meerdere browsertabbladen opent, heeft elk tabblad een eigen onafhankelijke versie van de gegevens.

Notitie

localStorage en sessionStorage kunnen worden gebruikt in Blazor WebAssembly apps, maar alleen door aangepaste code te schrijven of een pakket van derden te gebruiken.

Over het algemeen is sessionStorage veiliger te gebruiken. sessionStorage het risico voorkomt dat een gebruiker meerdere tabbladen opent en het volgende tegenkomt:

  • Fouten in statusopslag op tabbladen.
  • Verwarrend gedrag wanneer een tabblad de status van andere tabbladen overschrijft.

localStorage is de betere keuze als de app de status moet behouden bij het sluiten en opnieuw openen van de browser.

Waarschuwing

Gebruikers kunnen de gegevens bekijken of bewerken die zijn opgeslagen in localStorage en sessionStorage.

Containerservice voor status in het geheugen

Geneste onderdelen binden gegevens doorgaans met behulp van gekoppelde binding zoals beschreven in ASP.NET Core Blazor gegevensbinding. Geneste en niet-geneste onderdelen kunnen toegang tot gegevens delen met behulp van een geregistreerde in-memory statuscontainer. Een aangepaste statuscontainerklasse kan een toewijsbare Action gebruiken om onderdelen in verschillende delen van de app met statuswijzigingen op de hoogte te stellen. In het volgende voorbeeld:

  • Een paar onderdelen maakt gebruik van een statuscontainer om een eigenschap bij te houden.
  • Het ene onderdeel in het volgende voorbeeld is genest in het andere onderdeel, maar nesten is niet vereist voor deze aanpak.

Belangrijk

In het voorbeeld in deze sectie ziet u hoe u een containerservice in het geheugen maakt, de service registreert en de service in onderdelen gebruikt. In het voorbeeld worden geen gegevens bewaard zonder verdere ontwikkeling. Voor permanente opslag van gegevens moet de statuscontainer een onderliggend opslagmechanisme aannemen dat overleeft wanneer het browsergeheugen wordt gewist. Dit kan worden bereikt met localStorage/sessionStorage of een andere technologie.

StateContainer.cs:

public class StateContainer
{
    private string? savedString;

    public string Property
    {
        get => savedString ?? string.Empty;
        set
        {
            savedString = value;
            NotifyStateChanged();
        }
    }

    public event Action? OnChange;

    private void NotifyStateChanged() => OnChange?.Invoke();
}

Apps aan de clientzijde (Program bestand):

builder.Services.AddSingleton<StateContainer>();

Apps aan de serverzijde (Program bestand, ASP.NET Core in .NET 6 of hoger):

builder.Services.AddScoped<StateContainer>();

Apps aan de serverzijde (Startup.ConfigureServices van Startup.cs, ASP.NET Core ouder dan 6.0):

services.AddScoped<StateContainer>();

Shared/Nested.razor:

@implements IDisposable
@inject StateContainer StateContainer

<h2>Nested component</h2>

<p>Nested component Property: <b>@StateContainer.Property</b></p>

<p>
    <button @onclick="ChangePropertyValue">
        Change the Property from the Nested component
    </button>
</p>

@code {
    protected override void OnInitialized()
    {
        StateContainer.OnChange += StateHasChanged;
    }

    private void ChangePropertyValue()
    {
        StateContainer.Property = 
            $"New value set in the Nested component: {DateTime.Now}";
    }

    public void Dispose()
    {
        StateContainer.OnChange -= StateHasChanged;
    }
}

StateContainerExample.razor:

@page "/state-container-example"
@implements IDisposable
@inject StateContainer StateContainer

<h1>State Container Example component</h1>

<p>State Container component Property: <b>@StateContainer.Property</b></p>

<p>
    <button @onclick="ChangePropertyValue">
        Change the Property from the State Container Example component
    </button>
</p>

<Nested />

@code {
    protected override void OnInitialized()
    {
        StateContainer.OnChange += StateHasChanged;
    }

    private void ChangePropertyValue()
    {
        StateContainer.Property = "New value set in the State " +
            $"Container Example component: {DateTime.Now}";
    }

    public void Dispose()
    {
        StateContainer.OnChange -= StateHasChanged;
    }
}

De voorgaande onderdelen implementeren IDisposableen de OnChange gemachtigden worden afgemeld bij de Dispose methoden, die door het framework worden aangeroepen wanneer de onderdelen worden verwijderd. Zie ASP.NET Core Razor levenscyclus van onderdelen voor meer informatie.

Aanvullende benaderingen

Bij het implementeren van aangepaste statusopslag is een handige methode om trapsgewijze waarden en parameterste gebruiken:

  • Om de toestand over meerdere componenten te gebruiken.
  • Als er slechts één statusobject op het hoogste niveau is om vast te houden.

Oplossen

Wanneer u een aangepaste statusbeheerservice gebruikt waar u statuswijzigingen wilt ondersteunen van buiten Blazorsynchronisatiecontext (bijvoorbeeld van een timer of een achtergrondservice), moeten alle verbruikende onderdelen de StateHasChanged oproep in ComponentBase.InvokeAsyncverpakken. Dit zorgt ervoor dat de wijzigingsmelding wordt verwerkt in de synchronisatiecontext van de renderer.

Wanneer de statusbeheerservice geen StateHasChanged aanroept in de synchronisatiecontext van Blazor, wordt de volgende fout gegenereerd:

System.InvalidOperationException: 'De huidige thread is niet gekoppeld aan de dispatcher. Gebruik InvokeAsync() om de uitvoering over te schakelen naar de Dispatcher bij het activeren van rendering of onderdeelstatus.'

Zie ASP.NET Core Razor component renderingvoor meer informatie en een voorbeeld van het oplossen van deze fout.

Aanvullende informatiebronnen