ASP.NET Core Blazor Správa stavu

Stav uživatele vytvořený v Blazor WebAssembly aplikaci je uložený v paměti prohlížeče.

Mezi příklady stavu uživatele v paměti prohlížeče patří:

  • Hierarchie instancí komponent a jejich poslední výstup vykreslování ve vykresleném uživatelském rozhraní.
  • Hodnoty polí a vlastností v instancích součástí.
  • Data uchovávaná v instancích služby pro vkládání závislostí (di) .
  • Hodnoty nastavené prostřednictvím volání interoperability JavaScript .

Když uživatel zavře a znovu otevře prohlížeč nebo znovu načte stránku, stav uživatele uložený v paměti prohlížeče bude ztracen.

Poznámka

chráněný prohlížeč Storage ( Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage obor názvů) spoléhá na ochranu dat ASP.NET Core a podporuje se jenom pro Blazor Server aplikace.

Trvalý stav napříč relacemi prohlížeče

Obecně platí, že se udržuje stav napříč relacemi prohlížeče, kde uživatelé aktivně vytvářejí data a nečtou jenom data, která už existují.

Aby bylo možné zachovat stav napříč relacemi prohlížeče, musí aplikace uchovávat data do jiného umístění úložiště než do paměti prohlížeče. Trvalost stavu není automatické. Je nutné provést kroky při vývoji aplikace k implementaci stavové trvalosti dat.

Trvalost dat se obvykle vyžaduje jenom pro stav vysoké hodnoty, který uživatelé vynaložené úsilím vytvořit. V následujících příkladech trvalého stavu ušetříte čas nebo pomůcky při komerčních činnostech:

  • Webové formuláře s více kroky: časově náročné pro uživatele, aby znovu zadal data pro několik dokončených kroků webového formuláře s více kroky, pokud dojde ke ztrátě jejich stavu. Uživatel ztratí stav v tomto scénáři, pokud přechází z formuláře a vrátí se později.
  • Nákupní košíky: všechny obchodní důležité komponenty aplikace, které představují potenciální tržby, se dají udržovat. Uživatel, který ztratí svůj stav, a tedy i jeho nákupní košík, může koupit méně produktů nebo služeb při návratu do webu později.

Aplikace může zachovat jenom stav aplikace. Uživatelská rozhraní nelze zachovat, například instance komponent a jejich stromy vykreslování. Komponenty a stromy vykreslování nejsou obecně serializovatelné. Aby bylo možné zachovat stav uživatelského rozhraní, jako jsou například rozbalené uzly ovládacího prvku stromového zobrazení, musí aplikace používat vlastní kód pro modelování chování uživatelského rozhraní jako serializovatelnýho stavu aplikace.

Kam zachovat stav

Pro trvalý stav existují společná umístění:

Úložiště na straně serveru

V případě trvalé trvalosti dat, která zahrnuje více uživatelů a zařízení, může aplikace používat nezávislé serverové úložiště, ke kterému se přistupoval prostřednictvím webového rozhraní API. Vaše možnosti jsou:

  • Blob Storage
  • Úložiště hodnot klíčů
  • Relační databáze
  • Table Storage

Po uložení dat se stav uživatele zachová a bude dostupný v jakékoli nové relaci prohlížeče.

Vzhledem k tomu Blazor WebAssembly , že aplikace běží zcela v prohlížeči uživatele, vyžadují další míry pro přístup k zabezpečeným externím systémům, jako jsou služby úložiště a databáze. Blazor WebAssembly aplikace jsou zabezpečené stejným způsobem jako aplikace s jedním stránkou (jednostránkové). aplikace obvykle ověřuje uživatele prostřednictvím OAuth / OpenID Připojení (OIDC) a pak spolupracuje se službami úložiště a databázemi prostřednictvím volání webového rozhraní API do aplikace na straně serveru. Aplikace na straně serveru vypravuje přenos dat mezi Blazor WebAssembly aplikací a službou úložiště nebo databází. Blazor WebAssemblyAplikace udržuje dočasné připojení k aplikaci na straně serveru, zatímco aplikace na straně serveru má trvalé připojení k úložišti.

Další informace naleznete v následujících zdrojích:

Další informace o možnostech Azure Data Storage najdete v následujících tématech:

URL

Pro přechodná data představující stav navigace modelujte data jako součást adresy URL. Mezi příklady stavu uživatele v adrese URL patří:

  • ID zobrazené entity
  • Aktuální číslo stránky v mřížce na stránkovaném webu

Pokud uživatel ručně znovu načte stránku, obsah panelu Adresa prohlížeče se zachová.

Informace o definování vzorů adres URL pomocí @page direktivy naleznete v tématu ASP.NET Core Blazor úsek .

Úložiště prohlížeče

Pro přechodná data, která uživatel aktivně vytváří, se běžně používané umístění úložiště nachází v prohlížeči localStorage a sessionStorage kolekcích:

  • localStorage je vymezen v okně prohlížeče. Pokud uživatel stránku znovu načte nebo zavře a znovu otevře prohlížeč, stav přetrvává. Pokud uživatel otevře více karet prohlížeče, stav se sdílí napříč kartami. Data přetrvají do localStorage doby, než je explicitně zrušeno.
  • sessionStorage je vymezen na kartu prohlížeče. Pokud uživatel znovu načte kartu, stav se přetrvá. Pokud uživatel zavře kartu nebo prohlížeč, dojde ke ztrátě stavu. Pokud uživatel otevře více karet prohlížeče, každá karta má svou vlastní nezávislou verzi dat.

Poznámka

localStorage a sessionStorage dá se použít v Blazor WebAssembly aplikacích, ale jenom pomocí psaní vlastního kódu nebo pomocí balíčku třetí strany.

Obecně sessionStorage je bezpečnější použít. sessionStorage Vyhněte se riziku, že uživatel otevře více karet a narazí na následující:

  • Chyby v úložišti stavů napříč kartami.
  • Matoucí chování, když karta Přepisuje stav jiných karet.

localStorage je lepší volbou, pokud aplikace musí zachovat stav v rámci zavírání a znovu otevřít prohlížeč.

Upozornění

Uživatelé mohou zobrazit nebo manipulovat s daty uloženými v localStorage a sessionStorage .

Služba kontejneru stavu v paměti

Vnořené komponenty obvykle vážou data pomocí zřetězovaných vazeb, jak je popsáno v části BlazorASP.NET Core datová vazba . Vnořené a nezatížené komponenty mohou sdílet přístup k datům pomocí registrovaného kontejneru stavu v paměti. Třída kontejneru vlastního stavu může pomocí přiřaditelného objektu upozornit komponenty v různých částech aplikace Action na změny stavu. V následujícím příkladu:

  • Dvojice komponent používá kontejner stavu ke sledování vlastnosti.
  • Jedna komponenta v následujícím příkladu je vnořená v druhé komponentě, ale vnoření není nutné, aby tento přístup fungoval.

StateContainer.cs:

using System;

public class StateContainer
{
    private string savedString;

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

    public event Action OnChange;

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

V Program.cs souboru ( Blazor WebAssembly ):

builder.Services.AddSingleton<StateContainer>();

V Program.cs ( ) ve ASP.NET Core Blazor Server 6.0 nebo novějším:

builder.Services.AddScoped<StateContainer>();

V Startup.ConfigureServices ( ) ve Blazor Server verzích ASP.NET Core verze starší než 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;
    }
}

Pages/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;
    }
}

Předchozí komponenty implementují a delegáti se odhlásí v metodách, které jsou volány architekturou při uvolnění IDisposable OnChange Dispose komponent. Další informace naleznete v tématu RazorASP.NET Core životní cyklus komponenty.

Další zdroje informací

Blazor Server je stavová architektura aplikace. Ve většině případů aplikace udržuje připojení k serveru. Stav uživatele je uložený v paměti serveru v okruhu.

Příklady stavu uživatele uchovávaného v okruhu zahrnují:

  • Hierarchie instancí komponent a jejich poslední výstup vykreslování ve vykresleném uživatelském rozhraní.
  • Hodnoty polí a vlastností v instancích součástí.
  • Data uchovávaná v instancích služby pro vkládání závislostí (di) , která jsou vymezena okruhu.

Stav uživatele se může také najít v proměnných JavaScriptu v paměti prohlížeče prostřednictvím volání interoperability JavaScriptu .

Pokud dojde ke ztrátě dočasného připojení k síti, Blazor pokusí se uživatel znovu připojit k původnímu okruhu s původním stavem. Ale opětovné připojení uživatele k původnímu okruhu v paměti serveru není vždycky možné:

  • Server nemůže trvale zachovávat odpojený okruh. Server musí uvolnit odpojený okruh po vypršení časového limitu nebo v případě, že je server pod tlakem paměti.
  • V prostředích pro nasazení s více servery a vyrovnáváním zatížení můžou jednotlivé servery selhat nebo je automaticky odebrat, když už nepotřebujete pro zpracování celkového objemu požadavků. V případě, že se uživatel pokusí znovu připojit, může dojít k nedostupnosti původních požadavků na zpracování serveru pro uživatele.
  • Uživatel může zavřít a znovu otevřít prohlížeč nebo znovu načíst stránku, což odstraní všechny stavy uchovávané v paměti prohlížeče. Například hodnoty proměnných JavaScriptu nastavené prostřednictvím volání interoperability JavaScript se ztratí.

Když se uživatel nedá znovu připojit k původnímu okruhu, uživatel dostane nový okruh s prázdným stavem. Jedná se o ekvivalent zavření a opětovného otevření desktopové aplikace.

Zachovat stav napříč okruhy

Obecně platí, že se udržuje stav napříč okruhy, kde uživatelé aktivně vytvářejí data, a ne jednoduše čtou data, která již existují.

Aby bylo možné zachovat stav napříč okruhy, musí aplikace uchovávat data do jiného umístění úložiště než z paměti serveru. Trvalost stavu není automatické. Je nutné provést kroky při vývoji aplikace k implementaci stavové trvalosti dat.

Trvalost dat se obvykle vyžaduje jenom pro stav vysoké hodnoty, který uživatelé vynaložené úsilím vytvořit. V následujících příkladech trvalého stavu ušetříte čas nebo pomůcky při komerčních činnostech:

  • Webové formuláře s více kroky: časově náročné pro uživatele, aby znovu zadal data pro několik dokončených kroků webového formuláře s více kroky, pokud dojde ke ztrátě jejich stavu. Uživatel ztratí stav v tomto scénáři, pokud přechází z formuláře a vrátí se později.
  • Nákupní košíky: všechny obchodní důležité komponenty aplikace, které představují potenciální tržby, se dají udržovat. Uživatel, který ztratí svůj stav, a tedy i jeho nákupní košík, může koupit méně produktů nebo služeb při návratu do webu později.

Aplikace může zachovat jenom stav aplikace. Uživatelská rozhraní nelze zachovat, například instance komponent a jejich stromy vykreslování. Komponenty a stromy vykreslování nejsou obecně serializovatelné. Aby bylo možné zachovat stav uživatelského rozhraní, jako jsou například rozbalené uzly ovládacího prvku stromového zobrazení, musí aplikace používat vlastní kód pro modelování chování uživatelského rozhraní jako serializovatelnýho stavu aplikace.

Kam zachovat stav

Pro trvalý stav existují společná umístění:

Úložiště na straně serveru

V případě trvalé trvalosti dat, která zahrnuje více uživatelů a zařízení, může aplikace používat úložiště na straně serveru. Vaše možnosti jsou:

  • Blob Storage
  • Úložiště hodnot klíčů
  • Relační databáze
  • Table Storage

Po uložení dat se stav uživatele zachová a bude dostupný v jakémkoli novém okruhu.

Další informace o možnostech Azure Data Storage najdete v následujících tématech:

URL

Pro přechodná data představující stav navigace modelujte data jako součást adresy URL. Mezi příklady stavu uživatele v adrese URL patří:

  • ID zobrazené entity
  • Aktuální číslo stránky v mřížce na stránkovaném webu

Obsah panelu Adresa prohlížeče se zachová:

  • Pokud uživatel ručně znovu načte stránku.
  • Pokud dojde k nedostupnosti webového serveru a uživatel je nucen znovu načíst stránku, aby se mohl připojit k jinému serveru.

Informace o definování vzorů adres URL pomocí @page direktivy naleznete v tématu ASP.NET Core Blazor úsek .

Úložiště prohlížeče

Pro přechodná data, která uživatel aktivně vytváří, se běžně používané umístění úložiště nachází v prohlížeči localStorage a sessionStorage kolekcích:

  • localStorage je vymezen v okně prohlížeče. Pokud uživatel stránku znovu načte nebo zavře a znovu otevře prohlížeč, stav přetrvává. Pokud uživatel otevře více karet prohlížeče, stav se sdílí napříč kartami. Data přetrvají do localStorage doby, než je explicitně zrušeno.
  • sessionStorage je vymezen na kartu prohlížeče. Pokud uživatel znovu načte kartu, stav se přetrvá. Pokud uživatel zavře kartu nebo prohlížeč, dojde ke ztrátě stavu. Pokud uživatel otevře více karet prohlížeče, každá karta má svou vlastní nezávislou verzi dat.

Obecně sessionStorage je bezpečnější použít. sessionStorage Vyhněte se riziku, že uživatel otevře více karet a narazí na následující:

  • Chyby v úložišti stavů napříč kartami.
  • Matoucí chování, když karta Přepisuje stav jiných karet.

localStorage je lepší volbou, pokud aplikace musí zachovat stav v rámci zavírání a znovu otevřít prohlížeč.

Upozornění pro použití úložiště prohlížeče:

  • Podobně jako při použití databáze na straně serveru je načítání a ukládání dat asynchronní.
  • Na rozdíl od databáze na straně serveru není úložiště během předgenerování k dispozici, protože požadovaná stránka v prohlížeči neexistuje během fáze předvykreslování.
  • Storage o několika kilobajtech dat je pro aplikace vhodné zachovat Blazor Server . Po několika kilobajtech je potřeba vzít v úvahu dopad na výkon, protože data se načítají a ukládají v síti.
  • Uživatelé můžou data zobrazit nebo s nimi manipulovat. ochrana dat ASP.NET Core může riziko zmírnit. například ASP.NET Core chráněný prohlížeč Storage používá ASP.NET Core ochranu dat.

balíčky NuGet třetích stran poskytují rozhraní api pro práci s localStorage a sessionStorage . je vhodné zvážit výběr balíčku, který transparentně používá ASP.NET Core ochranu dat. Ochrana dat šifruje uložená data a snižuje potenciální riziko manipulace s uloženými daty. Pokud jsou data serializovaná do formátu JSON uložená v prostém textu, uživatelé můžou data zobrazit pomocí vývojářských nástrojů pro prohlížeč a také upravovat uložená data. Zabezpečení dat se vždy nejedná o problém, protože data můžou být triviální v podstatě. Například čtení nebo úprava uložené barvy prvku uživatelského rozhraní není významným bezpečnostním rizikem pro uživatele nebo organizaci. Neumožňuje uživatelům kontrolovat citlivá data a manipulovat s nimi.

ASP.NET Core Chráněný prohlížeč Storage

ASP.NET Core chráněný prohlížeč Storage využívá ASP.NET Core ochrany dat pro localStorage a sessionStorage .

Poznámka

chráněný prohlížeč Storage spoléhá na ochranu dat ASP.NET Core a podporuje se jenom pro Blazor Server aplikace.

Ukládání a načítání dat v rámci součásti

V každé součásti, která vyžaduje načtení nebo uložení dat do úložiště prohlížeče, použijte @inject direktivu pro vložení instance některého z následujících prvků:

  • ProtectedLocalStorage
  • ProtectedSessionStorage

Volba závisí na tom, jaké umístění úložiště prohlížeče chcete použít. V následujícím příkladu sessionStorage se používá:

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

@usingDirektiva může být umístěna do _Imports.razor souboru aplikace namísto do součásti. Použití _Imports.razor souboru zpřístupňuje obor názvů pro větší segmenty aplikace nebo celé aplikace.

Chcete-li zachovat currentCount hodnotu v Counter součásti aplikace na základě Blazor Server šablony projektu, upravte IncrementCount metodu, kterou chcete použít ProtectedSessionStore.SetAsync :

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

Ve větších a realističtějších aplikacích je ukládání jednotlivých polí nepravděpodobné. Pro aplikace je pravděpodobnější ukládání celých objektů modelu, které zahrnují komplexní stav. ProtectedSessionStore automaticky serializace a deserializace dat JSON pro ukládání komplexních objektů stavu.

V předchozím příkladu kódu currentCount se data ukládají jako sessionStorage['count'] v prohlížeči uživatele. data nejsou uložená v prostém textu, ale místo toho jsou chráněná pomocí ASP.NET Core ochrany dat. Zašifrovaná data lze zkontrolovat, pokud sessionStorage['count'] je vyhodnocena v konzole pro vývojáře v prohlížeči.

Chcete-li obnovit currentCount data, pokud se uživatel vrátí do Counter komponenty později, včetně toho, jestli je uživatel na novém okruhu, použijte ProtectedSessionStore.GetAsync :

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

Pokud parametry komponenty obsahují stav navigace, zavolejte ProtectedSessionStore.GetAsync a přiřaďte null nevýsledek v OnParametersSetAsync , nikoli OnInitializedAsync . OnInitializedAsync se volá pouze jednou při prvním vytvoření instance komponenty. OnInitializedAsync není voláno později, pokud uživatel přejde na jinou adresu URL a zůstane na stejné stránce. Další informace naleznete v tématu RazorASP.NET Core životní cyklus komponenty.

Upozornění

Příklady v této části fungují pouze v případě, že server nemá povolené předvykreslování. Pokud je povoleno předvykreslování, je vygenerována chyba s vysvětlením, že volání interoperability JavaScriptu nelze vydat, protože součást je předem vykreslena.

Buď zakažte předvykreslování nebo přidejte další kód pro práci s předvykreslováním. Další informace o psaní kódu, který funguje s předvykreslováním, najdete v části popisovač předvykreslování .

Zpracování stavu načítání

Vzhledem k tomu, že je úložiště prohlížeče přístupné asynchronně přes síťové připojení, je vždy časový interval před načtením dat a k dispozici pro komponentu. Nejlepších výsledků dosáhnete, když při načítání vykreslíte zprávu o stavu načítání, místo aby se zobrazila prázdná nebo výchozí data.

Jedním z těchto způsobů je sledovat, jestli data jsou null , což znamená, že se data pořád načítají. Ve výchozí Counter součásti je počet uchováván v int . Převést na typ s currentCount možnou hodnotou null přidáním otazníku ( ? ) do typu ( int ):

private int? currentCount;

Namísto nepodmíněného zobrazení čítače a Increment tlačítka zobrazte tyto prvky pouze v případě, že jsou data načtena kontrolou HasValue :

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

Zpracovat předvykreslování

Během předvykreslování:

  • Interaktivní připojení k prohlížeči uživatele neexistuje.
  • Prohlížeč ještě nemá stránku, ve které může spustit kód jazyka JavaScript.

localStorage nebo sessionStorage nejsou k dispozici během předběžného vykreslení. Pokud se komponenta pokusí o interakci s úložištěm, vygeneruje se chyba vysvětlující, že volání zprostředkovatele komunikace JavaScriptu nelze provést, protože komponenta je předem vykreslená.

Jedním ze způsob, jak chybu vyřešit, je zakázat prerendering. Obvykle je to nejlepší volba, pokud aplikace velmi často používá úložiště založené na prohlížeči. Předběžné vykreslení zvyšuje složitost a nevyužití výhod aplikace, protože aplikace nemůže předem vykreslovat žádný užitečný obsah, dokud localStorage nebudou k dispozici nebo nebudou k sessionStorage dispozici.

Pokud chcete zakázat předběžné vykreslení, otevřete soubor a změňte atribut pomocníka značky Pages/_Host.cshtml render-mode komponenty na Server :

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

Předběžné vykreslení <head> obsahu je v souboru Pages/_Layout.cshtml zakázané:

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

Předběžné vykreslení může být užitečné pro jiné stránky, které nepoužití localStorage nebo sessionStorage . Pokud chcete zachovat předběžné vykreslení, odložte operaci načítání, dokud se prohlížeč ne připojí k okruhu. Následuje příklad uložení hodnoty čítače:

@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);
    }
}

Zvážte zachování stavu do společného umístění.

Pokud mnoho komponent závisí na úložišti založeném na prohlížeči, re-implementing state provider code many times vytváří duplikaci kódu. Jednou z možností, jak zabránit duplikaci kódu, je vytvořit nadřazenou komponentu zprostředkovatele stavu, která zapouzdřuje logiku zprostředkovatele stavu. Podřízené komponenty mohou pracovat s trvalými daty bez ohledu na mechanismus trvalosti stavu.

V následujícím příkladu komponenty CounterStateProvider jsou data čítače trvale uložena v sessionStorage :

@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 OnInitializedAsync()
    {
        var result = await ProtectedSessionStore.GetAsync<int>("count");
        CurrentCount = result.Success ? result.Value : 0;
        isLoaded = true;
    }

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

Komponenta CounterStateProvider zpracovává fázi načítání tím, že nevykresluje svůj podřízený obsah, dokud se načítání nedokončí.

Chcete-li CounterStateProvider použít komponentu, zabalte instanci komponenty kolem jakékoli jiné součásti, která vyžaduje přístup ke stavu čítače. Pokud chcete, aby byl stav přístupný pro všechny komponenty v aplikaci, zabalte komponentu kolem komponenty CounterStateProvider Router ( App App.razor ):

<CounterStateProvider>
    <Router AppAssembly="@typeof(Program).Assembly">
        ...
    </Router>
</CounterStateProvider>

Zabalené komponenty přijímají a mohou upravovat trvalý stav čítače. Následující Counter komponenta implementuje vzor:

@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()
    {
        CounterStateProvider.CurrentCount++;
        await CounterStateProvider.SaveChangesAsync();
    }
}

Předchozí komponenta není nutná k interakci s , ani se nezachází ProtectedBrowserStorage s fází načítání.

Pokud chcete pracovat s předkreslovacími objekty, jak je popsáno výše, je možné je změnit tak, aby všechny komponenty, které využívají data čítačů, automaticky fungovaly CounterStateProvider s prerenderingem. Další informace najdete v části Zpracování předkreslování.

Obecně se doporučuje vzor nadřazené komponenty zprostředkovatele stavu:

  • Ke spotřebování stavu napříč mnoha komponentami.
  • Pokud existuje pouze jeden objekt stavu nejvyšší úrovně, který se má zachovat.

Pokud chcete zachovat mnoho různých objektů stavu a využívat různé podmnožiny objektů na různých místech, je lepší se vyhnout zachování stavu globálně.

Služba kontejneru stavu v paměti

Vnořené komponenty obvykle vážou data pomocí zřetězovaných vazeb, jak je popsáno v části BlazorASP.NET Core datová vazba . Vnořené a nezatížené komponenty mohou sdílet přístup k datům pomocí registrovaného kontejneru stavu v paměti. Třída kontejneru vlastního stavu může pomocí přiřaditelného objektu upozornit komponenty v různých částech aplikace Action na změny stavu. V následujícím příkladu:

  • Dvojice komponent používá kontejner stavu ke sledování vlastnosti.
  • Jedna komponenta v následujícím příkladu je vnořená v druhé komponentě, ale vnoření není nutné, aby tento přístup fungoval.

StateContainer.cs:

using System;

public class StateContainer
{
    private string savedString;

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

    public event Action OnChange;

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

V Program.cs souboru ( Blazor WebAssembly ):

builder.Services.AddSingleton<StateContainer>();

V Program.cs ( ) ve ASP.NET Core Blazor Server 6.0 nebo novějším:

builder.Services.AddScoped<StateContainer>();

V Startup.ConfigureServices ( ) ve Blazor Server verzích ASP.NET Core verze starší než 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;
    }
}

Pages/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;
    }
}

Předchozí komponenty implementují a delegáti se odhlásí v metodách, které jsou volány architekturou při uvolnění IDisposable OnChange Dispose komponent. Další informace naleznete v tématu RazorASP.NET Core životní cyklus komponenty.

Stav uživatele vytvořený v Blazor WebAssembly aplikaci se nachází v paměti prohlížeče.

Mezi příklady stavu uživatele v paměti prohlížeče patří:

Když uživatel zavře a znovu otevře prohlížeč nebo znovu načte stránku, stav uživatele v paměti prohlížeče se ztratí.

Poznámka

Funkce Protected Browser Storage ( obor názvů) využívá ASP.NET Core Data Protection a podporuje se Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage jenom pro Blazor Server aplikace.

Zachování stavu mezi relacemi prohlížeče

Obecně platí, že udržujte stav napříč relacemi prohlížeče, kde uživatelé aktivně vytvářejí data, a ne jednoduše čtou data, která už existují.

Pokud chcete zachovat stav mezi relacemi prohlížeče, musí aplikace zachovat data do jiného umístění úložiště, než je paměť prohlížeče. Trvalost stavu není automatická. Při vývoji aplikace musíte podniknou kroky k implementaci trvalosti stavových dat.

Trvalost dat se obvykle vyžaduje pouze pro stav vysoké hodnoty, který uživatelé vynaložit úsilí na vytvoření. V následujících příkladech zachování stavu šetří čas nebo pomáhá při komerčních aktivitách:

  • Vícekrokové webové formuláře: V případě ztráty stavu uživatele je časově náročné zadat data pro několik dokončených kroků vícekrokového webového formuláře. Pokud uživatel přejde z formuláře a později se vrátí, ztratí v tomto scénáři stav.
  • Nákupní košíky: Je možné udržovat libovolnou komerčně důležitou komponentu aplikace, která představuje potenciální výnosy. Uživatel, který přijde o svůj stav, a tedy nákupní košík, může po pozdějším návratu na web zakoupit méně produktů nebo služeb.

Aplikace může zachovat pouze stav aplikace. UI není možné zachovat, například instance komponent a jejich stromy vykreslování. Komponenty a stromy vykreslování nejsou obecně serializovatelné. K zachování stavu uživatelského rozhraní, jako jsou rozbalené uzly ovládacího prvku stromového zobrazení, musí aplikace použít vlastní kód k modelování chování stavu uživatelského rozhraní jako serializovatelného stavu aplikace.

Kde zachovat stav

Existují běžná umístění pro trvalý stav:

Úložiště na straně serveru

Pro trvalou trvalost dat, která zahrnuje více uživatelů a zařízení, může aplikace používat nezávislé úložiště na straně serveru, ke kterým se přistupuje prostřednictvím webového rozhraní API. Vaše možnosti jsou:

  • Blob Storage
  • Úložiště klíč-hodnota
  • Relační databáze
  • Table Storage

Po uložení dat se stav uživatele zachová a bude dostupný v jakékoli nové relaci prohlížeče.

Vzhledem k tomu, že aplikace běží zcela v prohlížeči uživatele, vyžadují další opatření pro přístup k zabezpečeným externím systémům, jako jsou služby úložiště a Blazor WebAssembly databáze. Blazor WebAssembly Aplikace jsou zabezpečeny stejným způsobem jako jedno stránkové aplikace (SPA). Aplikace obvykle ověřuje uživatele prostřednictvím OAuth / OpenID Připojení (OIDC) a pak komunikuje se službami úložiště a databázemi prostřednictvím volání webového rozhraní API do aplikace na straně serveru. Aplikace na straně serveru zprostředkuje přenos dat mezi aplikací a Blazor WebAssembly službou úložiště nebo databází. Aplikace udržuje dočasné připojení k aplikaci na straně serveru, zatímco aplikace na straně serveru má trvalé Blazor WebAssembly připojení k úložišti.

Další informace naleznete v následujících zdrojích:

Další informace o možnostech úložiště dat Azure najdete zde:

URL

Pro přechodná data představující stav navigace modelujete data jako součást adresy URL. Příklady stavu uživatele modelované v adrese URL:

  • ID z viewed entity
  • Číslo aktuální stránky ve stránkované mřížce

Obsah adresního řádku prohlížeče se zachová, pokud uživatel stránku znovu ručně načte.

Informace o definování vzorů adres URL pomocí @page direktivy najdete v tématu ASP.NET Core Blazor úsek .

Úložiště prohlížeče

Pro přechodná data, která uživatel aktivně vytváří, se běžně používá umístění úložiště v prohlížeči a localStorage sessionStorage kolekce:

  • localStorage je vymezený na okno prohlížeče. Pokud uživatel znovu načte stránku nebo zavře a znovu otevře prohlížeč, stav se zachová. Pokud uživatel otevře více karet prohlížeče, stav se na kartách sdílí. Data se uchová v , localStorage dokud není explicitně vymazáno.
  • sessionStorage je vymezený na kartu prohlížeče. Pokud uživatel znovu načte kartu, stav se zachová. Pokud uživatel kartu nebo prohlížeč zavře, stav se ztratí. Pokud uživatel otevře více karet prohlížeče, každá karta má svou vlastní nezávislou verzi dat.

Poznámka

localStorage a sessionStorage lze použít v Blazor WebAssembly aplikacích, ale pouze napsání vlastního kódu nebo použití balíčku třetí strany.

Obecně platí, sessionStorage že použití je bezpečnější. sessionStorage zabraňuje riziku, že uživatel otevře více karet a narazí na následující:

  • Chyby v úložišti stavů na kartách
  • Matoucí chování při přepsání stavu jiných karet na kartě

localStorage je lepší volbou v případě, že aplikace musí zachovat stav po zavření a opětovném otevření prohlížeče.

Upozornění

Uživatelé mohou zobrazit nebo manipulovat s daty uloženými v a localStorage sessionStorage .

Služba kontejneru stavu v paměti

Vnořené komponenty obvykle vážou data pomocí zřetězovaných vazeb, jak je popsáno v části BlazorASP.NET Core datová vazba . Vnořené a nezatížené komponenty mohou sdílet přístup k datům pomocí registrovaného kontejneru stavu v paměti. Třída kontejneru vlastního stavu může pomocí přiřaditelného objektu upozornit komponenty v různých částech aplikace Action na změny stavu. V následujícím příkladu:

  • Dvojice komponent používá kontejner stavu ke sledování vlastnosti.
  • Jedna komponenta v následujícím příkladu je vnořená v druhé komponentě, ale vnoření není nutné, aby tento přístup fungoval.

StateContainer.cs:

using System;

public class StateContainer
{
    private string savedString;

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

    public event Action OnChange;

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

V Program.cs souboru ( Blazor WebAssembly ):

builder.Services.AddSingleton<StateContainer>();

V Program.cs ( ) ve ASP.NET Core Blazor Server 6.0 nebo novějším:

builder.Services.AddScoped<StateContainer>();

V Startup.ConfigureServices ( ) ve Blazor Server verzích ASP.NET Core verze starší než 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;
    }
}

Pages/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;
    }
}

Předchozí komponenty implementují a delegáti se odhlásí v metodách, které jsou volány architekturou při uvolnění IDisposable OnChange Dispose komponent. Další informace naleznete v tématu RazorASP.NET Core životní cyklus komponenty.

Další zdroje informací

Blazor Server je architekturou stavových aplikací. Ve většině času udržuje aplikace připojení k serveru. Stav uživatele se nachází v paměti serveru v okruhu.

Příklady stavu uživatele v okruhu:

  • Hierarchie instancí komponent a jejich nejnovější výstup vykreslení ve vykreslených uživatelském rozhraní.
  • Hodnoty polí a vlastností v instancích komponent.
  • Data uchovaná v instancích služby injektáže závislostí ,které jsou vymezené na okruh.

Stav uživatele se také může najít v proměnných JavaScriptu v paměti nastavené v prohlížeči prostřednictvím volání zprostředkovatele komunikace JavaScriptu.

Pokud dojde k dočasné ztrátě síťového připojení, pokusí se uživatele znovu připojit ke svému původnímu okruhu Blazor s původním stavem. Opětovné připojení uživatele k původnímu okruhu v paměti serveru ale není vždy možné:

  • Server nemůže uchovat odpojený okruh navždy. Server musí uvolnit odpojený okruh po uplynutí časového limitu nebo v případě, že je server pod tlak na paměť.
  • V prostředích nasazení s více servery s vyrovnáváním zatížení může dojít k selhání nebo automatickému odebrání jednotlivých serverů, pokud už nejsou potřeba ke zpracování celkového objemu požadavků. Když se uživatel pokusí o opětovné připojení, může se stát, že původní server zpracovává požadavky na uživatele.
  • Uživatel může prohlížeč zavřít a znovu otevřít nebo znovu načíst stránku, což odebere všechny stavy v paměti prohlížeče. Například hodnoty proměnných JavaScriptu nastavené prostřednictvím volání zprostředkovatele komunikace JavaScriptu se ztratí.

Když se uživatel nemůže znovu připojit ke svému původnímu okruhu, obdrží nový okruh s prázdným stavem. To je ekvivalentem k zavření a opětovnému otevření desktopové aplikace.

Zachování stavu napříč okruhy

Obecně platí, že udržujte stav napříč okruhy, kde uživatelé aktivně vytvářejí data, a ne jednoduše čtou data, která už existují.

Pokud chcete zachovat stav napříč okruhy, aplikace musí zachovat data do jiného umístění úložiště, než je paměť serveru. Trvalost stavu není automatická. Při vývoji aplikace musíte podniknou kroky k implementaci trvalosti stavových dat.

Trvalost dat se obvykle vyžaduje pouze pro stav vysoké hodnoty, který uživatelé vynaložit úsilí na vytvoření. V následujících příkladech zachování stavu šetří čas nebo pomáhá při komerčních aktivitách:

  • Vícekrokové webové formuláře: V případě ztráty stavu uživatele je časově náročné zadat data pro několik dokončených kroků vícekrokového webového formuláře. Pokud uživatel přejde z formuláře a později se vrátí, ztratí v tomto scénáři stav.
  • Nákupní košíky: Je možné udržovat libovolnou komerčně důležitou komponentu aplikace, která představuje potenciální výnosy. Uživatel, který přijde o svůj stav, a tedy nákupní košík, může po návratu na web zakoupit méně produktů nebo služeb.

Aplikace může zachovat pouze stav aplikace. UI není možné zachovat, například instance komponent a jejich stromy vykreslování. Komponenty a stromy vykreslování nejsou obecně serializovatelné. K zachování stavu uživatelského rozhraní, jako jsou rozbalené uzly ovládacího prvku stromového zobrazení, musí aplikace použít vlastní kód k modelování chování stavu uživatelského rozhraní jako serializovatelného stavu aplikace.

Kde zachovat stav

Existují běžná umístění pro trvalý stav:

Úložiště na straně serveru

Pro trvalou trvalost dat, která zahrnuje více uživatelů a zařízení, může aplikace používat úložiště na straně serveru. Vaše možnosti jsou:

  • Blob Storage
  • Úložiště klíč-hodnota
  • Relační databáze
  • Table Storage

Po uložení dat se stav uživatele zachová a bude dostupný v každém novém okruhu.

Další informace o možnostech úložiště dat Azure najdete zde:

URL

Pro přechodná data představující stav navigace modelujete data jako součást adresy URL. Příklady stavu uživatele modelované v adrese URL:

  • ID z viewed entity
  • Číslo aktuální stránky ve stránkované mřížce

Obsah adresního řádku prohlížeče se zachová:

  • Pokud uživatel stránku znovu ručně načte.
  • Pokud webový server přestane být dostupný a uživatel musí stránku znovu načíst, aby se mohl připojit k jinému serveru.

Informace o definování vzorů adres URL pomocí @page direktivy najdete v tématu ASP.NET Core Blazor úsek .

Úložiště prohlížeče

Pro přechodná data, která uživatel aktivně vytváří, se běžně používá umístění úložiště v prohlížeči a localStorage sessionStorage kolekce:

  • localStorage je vymezený na okno prohlížeče. Pokud uživatel znovu načte stránku nebo zavře a znovu otevře prohlížeč, stav se zachová. Pokud uživatel otevře více karet prohlížeče, stav se na kartách sdílí. Data se uchová, localStorage dokud není explicitně vymazáno.
  • sessionStorage je vymezený na kartu prohlížeče. Pokud uživatel znovu načte kartu, stav se zachová. Pokud uživatel kartu nebo prohlížeč zavře, stav se ztratí. Pokud uživatel otevře více karet prohlížeče, každá karta má svou vlastní nezávislou verzi dat.

Obecně platí, sessionStorage že použití je bezpečnější. sessionStorage zabraňuje riziku, že uživatel otevře více karet a narazí na následující:

  • Chyby v úložišti stavů na kartách
  • Matoucí chování při přepsání stavu jiných karet na kartě

localStorage je lepší volbou v případě, že aplikace musí zachovat stav po zavření a opětovném otevření prohlížeče.

Upozornění pro používání úložiště prohlížeče:

  • Podobně jako při použití databáze na straně serveru je načítání a ukládání dat asynchronní.
  • Na rozdíl od databáze na straně serveru není úložiště během předběžného vykreslení k dispozici, protože požadovaná stránka v prohlížeči během fáze předběžného vykreslení neexistuje.
  • Storage několik kilobajtů dat je rozumné u aplikací Blazor Server zachovat. Nad rámec několika kilobajtů musíte zvážit vliv na výkon, protože data se načítá a ukládají v síti.
  • Uživatelé mohou data zobrazit nebo s daty manipulovat. ASP.NET Core Data Protection může riziko zmírnit. Například ASP.NET Core Protected Browser Storage ASP.NET Core Data Protection.

Balíčky třetích NuGet poskytují rozhraní API pro práci s a localStorage sessionStorage . Je vhodné zvážit výběr balíčku, který transparentně využívá ASP.NET Core Data Protection. Ochrana dat šifruje uložená data a snižuje potenciální riziko manipulace s uloženými daty. Pokud jsou data serializovaná do formátu JSON uložená v prostém textu, uživatelé je mohou zobrazit pomocí vývojářských nástrojů prohlížeče a také upravit uložená data. Zabezpečení dat není vždy problém, protože data mohou být ze povahy triviální. Například čtení nebo úprava uložené barvy prvku uživatelského rozhraní není pro uživatele nebo organizaci významným bezpečnostním rizikem. Vyhněte se tomu, aby uživatelé měli možnost kontrolovat citlivá data nebo s ním manipulovat.

ASP.NET Core Protected Browser Storage

ASP.NET Core Protected Browser Storage využívá ASP.NET Core Data Protection pro a localStorage sessionStorage .

Poznámka

Funkce Protected Browser Storage spoléhá na ASP.NET Core Data Protection a podporuje se jenom pro Blazor Server aplikace.

Uložení a načtení dat v rámci komponenty

V každé komponentě, která vyžaduje načtení nebo uložení dat do úložiště prohlížeče, použijte direktivu k vložení instance jedné @inject z následujících možností:

  • ProtectedLocalStorage
  • ProtectedSessionStorage

Volba závisí na tom, které umístění úložiště prohlížeče chcete použít. V následujícím příkladu sessionStorage se používá :

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

Direktiva @using může být umístěna v souboru _Imports.razor aplikace místo v komponentě . Použitím tohoto souboru je obor názvů dostupný pro _Imports.razor větší segmenty aplikace nebo celé aplikace.

Pokud chcete zachovat hodnotu v komponentě aplikace na základě currentCount šablony Counter Blazor Server projektu, upravte IncrementCount metodu tak, aby se používá ProtectedSessionStore.SetAsync :

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

Ve větších, realističtějších aplikacích není ukládání jednotlivých polí pravděpodobný scénář. Aplikace budou pravděpodobně ukládat celé objekty modelu, které obsahují složitý stav. ProtectedSessionStore automaticky serializuje a deserializuje data JSON pro ukládání objektů komplexního stavu.

V předchozím příkladu kódu se currentCount data ukládají jako v sessionStorage['count'] prohlížeči uživatele. Data se neuchovávají v prostém textu, ale jsou chráněná pomocí ASP.NET Core Data Protection. Šifrovaná data je možné zkontrolovat, pokud se vyhodnocují v konzole pro vývojáře sessionStorage['count'] v prohlížeči.

Pokud se uživatel k komponentě vrátí později, včetně toho, jestli je uživatel v novém okruhu, obnovte currentCount Counter data pomocí ProtectedSessionStore.GetAsync :

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

Pokud parametry komponenty obsahují stav navigace, ProtectedSessionStore.GetAsync zavolejte a přiřaďte výsledek, který není null výsledkem , nikoli OnParametersSetAsync OnInitializedAsync . OnInitializedAsync se volá pouze jednou při první instanci komponenty. OnInitializedAsync pokud uživatel přejde na jinou adresu URL a zůstane na stejné stránce, nebude se později volat. Další informace naleznete v tématu RazorASP.NET Core životní cyklus komponenty.

Upozornění

Příklady v této části fungují jenom v případě, že server nemá povolené předběžné vykreslení. Při povoleném předběžném vykreslení se vygeneruje chyba vysvětlující, že volání zprostředkovatele komunikace JavaScriptu nelze provést, protože komponenta se prerenderuje.

Zakažte předběžné vykreslení nebo přidejte další kód pro práci s prerenderingem. Další informace o psaní kódu, který funguje s prerenderingem, najdete v části Zpracování prerenderingu.

Zpracování stavu načítání

Vzhledem k tomu, že se k úložišti prohlížeče přistupuje asynchronně přes síťové připojení, existuje vždy určité časové období, než se data načítá a jsou pro komponentu dostupná. Nejlepších výsledků dosáhnete, když během načítání vykreslíte zprávu o stavu načítání místo zobrazování prázdných nebo výchozích dat.

Jedním z přístupů je sledovat, jestli data jsou , což znamená, že null se data stále načítají. Ve výchozí Counter komponentě se počet nachází v int . Přidání currentCount otazníku ( ) k typu ( ) na hodnotu null ? int

private int? currentCount;

Místo nepodmíněného zobrazení počtu a tlačítka zobrazte tyto prvky pouze v případě, že Increment jsou data načtena kontrolou HasValue :

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

Zpracování předběžného vykreslení

Během předběžného vykreslení:

  • Interaktivní připojení k prohlížeči uživatele neexistuje.
  • Prohlížeč ještě nemá stránku, na které by mohl spustit javascriptový kód.

localStorage nebo sessionStorage nejsou k dispozici během předběžného vykreslení. Pokud se komponenta pokusí o interakci s úložištěm, vygeneruje se chyba vysvětlující, že volání zprostředkovatele komunikace JavaScriptu nelze provést, protože komponenta je předem vykreslená.

Jedním ze způsob, jak chybu vyřešit, je zakázat prerendering. Obvykle je to nejlepší volba, pokud aplikace velmi často používá úložiště založené na prohlížeči. Předběžné vykreslení zvyšuje složitost a nevyužití výhod aplikace, protože aplikace nemůže předem vykreslovat žádný užitečný obsah, dokud localStorage nebudou k dispozici nebo nebudou k sessionStorage dispozici.

Pokud chcete zakázat předběžné vykreslení, otevřete soubor a změňte atribut pomocníka značky Pages/_Host.cshtml render-mode komponenty na Server :

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

Předběžné vykreslení může být užitečné pro jiné stránky, které nepoužití localStorage nebo sessionStorage . Pokud chcete zachovat předběžné vykreslení, odložte operaci načítání, dokud se prohlížeč ne připojí k okruhu. Následuje příklad uložení hodnoty čítače:

@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);
    }
}

Vyvážte zachování stavu do společného umístění.

Pokud mnoho komponent závisí na úložišti založeném na prohlížeči, pře implementací kódu poskytovatele stavů mnohokrát dojde k duplikaci kódu. Jednou z možností, jak zabránit duplikaci kódu, je vytvořit nadřazenou komponentu zprostředkovatele stavu, která zapouzdřuje logiku zprostředkovatele stavu. Podřízené komponenty mohou pracovat s trvalými daty bez ohledu na mechanismus trvalosti stavu.

V následujícím příkladu komponenty CounterStateProvider jsou data čítače trvale uložena v sessionStorage :

@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 OnInitializedAsync()
    {
        var result = await ProtectedSessionStore.GetAsync<int>("count");
        CurrentCount = result.Success ? result.Value : 0;
        isLoaded = true;
    }

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

Komponenta CounterStateProvider zpracovává fázi načítání tím, že nevykresluje svůj podřízený obsah, dokud se načítání nedokončí.

Chcete-li CounterStateProvider použít komponentu, zabalte instanci komponenty kolem jakékoli jiné součásti, která vyžaduje přístup ke stavu čítače. Pokud chcete, aby byl stav přístupný pro všechny komponenty v aplikaci, zabalte komponentu kolem komponenty CounterStateProvider Router ( App App.razor ):

<CounterStateProvider>
    <Router AppAssembly="@typeof(Program).Assembly">
        ...
    </Router>
</CounterStateProvider>

Poznámka

S vydáním verze ASP.NET Core 5.0.1 a pro jakékoli další verze 5.x zahrnuje komponenta parametr Router PreferExactMatches nastavený na @true . Další informace naleznete v tématu Migrace z ASP.NET Core 3.1 na 5.0.

Zabalené komponenty přijímají a mohou upravovat trvalý stav čítače. Následující Counter komponenta implementuje vzor:

@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()
    {
        CounterStateProvider.CurrentCount++;
        await CounterStateProvider.SaveChangesAsync();
    }
}

Předchozí komponenta není nutná k interakci s , ani se nezachází ProtectedBrowserStorage s fází načítání.

Pokud chcete pracovat s předkreslovacími objekty, jak je popsáno výše, je možné je změnit tak, aby všechny komponenty, které využívají data čítačů, automaticky fungovaly CounterStateProvider s prerenderingem. Další informace najdete v části Zpracování předkreslování.

Obecně se doporučuje vzor nadřazené komponenty zprostředkovatele stavu:

  • Ke spotřebování stavu napříč mnoha komponentami.
  • Pokud existuje pouze jeden objekt stavu nejvyšší úrovně, který se má zachovat.

Pokud chcete zachovat mnoho různých objektů stavu a využívat různé podmnožiny objektů na různých místech, je lepší se vyhnout zachování stavu globálně.

Služba kontejneru stavu v paměti

Vnořené komponenty obvykle vážou data pomocí zřetězovaných vazeb, jak je popsáno v části BlazorASP.NET Core datová vazba . Vnořené a nezatížené komponenty mohou sdílet přístup k datům pomocí registrovaného kontejneru stavu v paměti. Třída kontejneru vlastního stavu může pomocí přiřaditelného objektu upozornit komponenty v různých částech aplikace Action na změny stavu. V následujícím příkladu:

  • Dvojice komponent používá kontejner stavu ke sledování vlastnosti.
  • Jedna komponenta v následujícím příkladu je vnořená v druhé komponentě, ale vnoření není nutné, aby tento přístup fungoval.

StateContainer.cs:

using System;

public class StateContainer
{
    private string savedString;

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

    public event Action OnChange;

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

V Program.cs souboru ( Blazor WebAssembly ):

builder.Services.AddSingleton<StateContainer>();

V Program.cs ( ) ve ASP.NET Core Blazor Server 6.0 nebo novějším:

builder.Services.AddScoped<StateContainer>();

V Startup.ConfigureServices ( ) ve Blazor Server verzích ASP.NET Core verze starší než 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;
    }
}

Pages/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;
    }
}

Předchozí komponenty implementují a delegáti se odhlásí v metodách, které jsou volány architekturou při uvolnění IDisposable OnChange Dispose komponent. Další informace naleznete v tématu RazorASP.NET Core životní cyklus komponenty.

Stav uživatele vytvořený v Blazor WebAssembly aplikaci se nachází v paměti prohlížeče.

Mezi příklady stavu uživatele v paměti prohlížeče patří:

Když uživatel zavře a znovu otevře prohlížeč nebo znovu načte stránku, stav uživatele v paměti prohlížeče se ztratí.

Poznámka

Funkce Protected Browser Storage ( obor názvů) spoléhá na ASP.NET Core Data Protection a podporuje se Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage jenom pro Blazor Server aplikace.

Zachování stavu mezi relacemi prohlížeče

Obecně platí, že udržujte stav napříč relacemi prohlížeče, kde uživatelé aktivně vytvářejí data, a ne jednoduše čtou data, která už existují.

Pokud chcete zachovat stav mezi relacemi prohlížeče, aplikace musí data uchovávat v jiném umístění úložiště, než je paměť prohlížeče. Trvalost stavu není automatická. Při vývoji aplikace musíte podniknou kroky k implementaci trvalosti stavových dat.

Trvalost dat se obvykle vyžaduje pouze pro stav vysoké hodnoty, který uživatelé vynaložit úsilí na vytvoření. V následujících příkladech zachování stavu šetří čas nebo pomáhá při komerčních aktivitách:

  • Vícekrokové webové formuláře: V případě ztráty stavu uživatele je časově náročné zadat data pro několik dokončených kroků vícekrokového webového formuláře. Pokud uživatel přejde z formuláře a později se vrátí, ztratí v tomto scénáři stav.
  • Nákupní košíky: Je možné udržovat libovolnou komerčně důležitou komponentu aplikace, která představuje potenciální výnosy. Uživatel, který přijde o svůj stav, a tedy nákupní košík, může po pozdějším návratu na web zakoupit méně produktů nebo služeb.

Aplikace může zachovat pouze stav aplikace. UI není možné zachovat, například instance komponent a jejich stromy vykreslování. Komponenty a stromy vykreslování nejsou obecně serializovatelné. K zachování stavu uživatelského rozhraní, jako jsou rozbalené uzly ovládacího prvku stromového zobrazení, musí aplikace použít vlastní kód k modelování chování stavu uživatelského rozhraní jako serializovatelného stavu aplikace.

Kde zachovat stav

Existují běžná umístění pro trvalý stav:

Úložiště na straně serveru

Pro trvalou trvalost dat, která zahrnuje více uživatelů a zařízení, může aplikace používat nezávislé úložiště na straně serveru, ke kterým se přistupuje prostřednictvím webového rozhraní API. Vaše možnosti jsou:

  • Blob Storage
  • Úložiště klíč-hodnota
  • Relační databáze
  • Table Storage

Po uložení dat se stav uživatele zachová a bude dostupný v jakékoli nové relaci prohlížeče.

Vzhledem k tomu, že aplikace běží zcela v prohlížeči uživatele, vyžadují další opatření pro přístup k zabezpečeným externím systémům, jako jsou služby úložiště a Blazor WebAssembly databáze. Blazor WebAssembly Aplikace jsou zabezpečeny stejným způsobem jako jedno stránkové aplikace (SPA). Aplikace obvykle ověřuje uživatele prostřednictvím OAuth / OpenID Připojení (OIDC) a pak komunikuje se službami úložiště a databázemi prostřednictvím volání webového rozhraní API do aplikace na straně serveru. Aplikace na straně serveru zprostředkuje přenos dat mezi aplikací a Blazor WebAssembly službou úložiště nebo databází. Aplikace udržuje dočasné připojení k aplikaci na straně serveru, zatímco aplikace na straně serveru má trvalé Blazor WebAssembly připojení k úložišti.

Další informace naleznete v následujících zdrojích:

Další informace o možnostech úložiště dat Azure najdete zde:

URL

Pro přechodná data představující stav navigace modelujte data jako součást adresy URL. Mezi příklady stavu uživatele v adrese URL patří:

  • ID zobrazené entity
  • Aktuální číslo stránky v mřížce na stránkovaném webu

Pokud uživatel ručně znovu načte stránku, obsah panelu Adresa prohlížeče se zachová.

Informace o definování vzorů adres URL pomocí @page direktivy naleznete v tématu ASP.NET Core Blazor úsek .

Úložiště prohlížeče

Pro přechodná data, která uživatel aktivně vytváří, se běžně používané umístění úložiště nachází v prohlížeči localStorage a sessionStorage kolekcích:

  • localStorage je vymezen v okně prohlížeče. Pokud uživatel stránku znovu načte nebo zavře a znovu otevře prohlížeč, stav přetrvává. Pokud uživatel otevře více karet prohlížeče, stav se sdílí napříč kartami. Data přetrvají do localStorage doby, než je explicitně zrušeno.
  • sessionStorage je vymezen na kartu prohlížeče. Pokud uživatel znovu načte kartu, stav se přetrvá. Pokud uživatel zavře kartu nebo prohlížeč, dojde ke ztrátě stavu. Pokud uživatel otevře více karet prohlížeče, každá karta má svou vlastní nezávislou verzi dat.

Poznámka

localStorage a sessionStorage dá se použít v Blazor WebAssembly aplikacích, ale jenom pomocí psaní vlastního kódu nebo pomocí balíčku třetí strany.

Obecně sessionStorage je bezpečnější použít. sessionStorage Vyhněte se riziku, že uživatel otevře více karet a narazí na následující:

  • Chyby v úložišti stavů napříč kartami.
  • Matoucí chování, když karta Přepisuje stav jiných karet.

localStorage je lepší volbou, pokud aplikace musí zachovat stav v rámci zavírání a znovu otevřít prohlížeč.

Upozornění

Uživatelé mohou zobrazit nebo manipulovat s daty uloženými v localStorage a sessionStorage .

Služba kontejneru stavu v paměti

Vnořené komponenty obvykle vážou data pomocí zřetězovaných vazeb, jak je popsáno v části BlazorASP.NET Core datová vazba . Vnořené a nezatížené komponenty mohou sdílet přístup k datům pomocí registrovaného kontejneru stavu v paměti. Třída kontejneru vlastního stavu může pomocí přiřaditelného objektu upozornit komponenty v různých částech aplikace Action na změny stavu. V následujícím příkladu:

  • Dvojice komponent používá kontejner stavu ke sledování vlastnosti.
  • Jedna komponenta v následujícím příkladu je vnořená v druhé komponentě, ale vnoření není nutné, aby tento přístup fungoval.

StateContainer.cs:

using System;

public class StateContainer
{
    private string savedString;

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

    public event Action OnChange;

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

V Program.cs souboru ( Blazor WebAssembly ):

builder.Services.AddSingleton<StateContainer>();

V Program.cs ( ) ve ASP.NET Core Blazor Server 6.0 nebo novějším:

builder.Services.AddScoped<StateContainer>();

V Startup.ConfigureServices ( ) ve Blazor Server verzích ASP.NET Core verze starší než 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;
    }
}

Pages/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;
    }
}

Předchozí komponenty implementují a delegáti se odhlásí v metodách, které jsou volány architekturou při uvolnění IDisposable OnChange Dispose komponent. Další informace naleznete v tématu RazorASP.NET Core životní cyklus komponenty.

Další zdroje informací

Blazor Server je stavová architektura aplikace. Ve většině případů aplikace udržuje připojení k serveru. Stav uživatele je uložený v paměti serveru v okruhu.

Příklady stavu uživatele uchovávaného v okruhu zahrnují:

  • Hierarchie instancí komponent a jejich poslední výstup vykreslování ve vykresleném uživatelském rozhraní.
  • Hodnoty polí a vlastností v instancích součástí.
  • Data uchovávaná v instancích služby pro vkládání závislostí (di) , která jsou vymezena okruhu.

Stav uživatele se může také najít v proměnných JavaScriptu v paměti prohlížeče prostřednictvím volání interoperability JavaScriptu .

Pokud dojde ke ztrátě dočasného připojení k síti, Blazor pokusí se uživatel znovu připojit k původnímu okruhu s původním stavem. Ale opětovné připojení uživatele k původnímu okruhu v paměti serveru není vždycky možné:

  • Server nemůže trvale zachovávat odpojený okruh. Server musí uvolnit odpojený okruh po vypršení časového limitu nebo v případě, že je server pod tlakem paměti.
  • V prostředích pro nasazení s více servery a vyrovnáváním zatížení můžou jednotlivé servery selhat nebo je automaticky odebrat, když už nepotřebujete pro zpracování celkového objemu požadavků. V případě, že se uživatel pokusí znovu připojit, může dojít k nedostupnosti původních požadavků na zpracování serveru pro uživatele.
  • Uživatel může zavřít a znovu otevřít prohlížeč nebo znovu načíst stránku, což odstraní všechny stavy uchovávané v paměti prohlížeče. Například hodnoty proměnných JavaScriptu nastavené prostřednictvím volání interoperability JavaScript se ztratí.

Když se uživatel nedá znovu připojit k původnímu okruhu, uživatel dostane nový okruh s prázdným stavem. Jedná se o ekvivalent zavření a opětovného otevření desktopové aplikace.

Zachovat stav napříč okruhy

Obecně platí, že se udržuje stav napříč okruhy, kde uživatelé aktivně vytvářejí data, a ne jednoduše čtou data, která již existují.

Aby bylo možné zachovat stav napříč okruhy, musí aplikace uchovávat data do jiného umístění úložiště než z paměti serveru. Trvalost stavu není automatické. Je nutné provést kroky při vývoji aplikace k implementaci stavové trvalosti dat.

Trvalost dat se obvykle vyžaduje jenom pro stav vysoké hodnoty, který uživatelé vynaložené úsilím vytvořit. V následujících příkladech trvalého stavu ušetříte čas nebo pomůcky při komerčních činnostech:

  • Webové formuláře s více kroky: časově náročné pro uživatele, aby znovu zadal data pro několik dokončených kroků webového formuláře s více kroky, pokud dojde ke ztrátě jejich stavu. Uživatel ztratí stav v tomto scénáři, pokud přechází z formuláře a vrátí se později.
  • Nákupní košíky: všechny obchodní důležité komponenty aplikace, které představují potenciální tržby, se dají udržovat. Uživatel, který ztratí svůj stav, a tedy i jeho nákupní košík, může koupit méně produktů nebo služeb při návratu do webu později.

Aplikace může zachovat jenom stav aplikace. Uživatelská rozhraní nelze zachovat, například instance komponent a jejich stromy vykreslování. Komponenty a stromy vykreslování nejsou obecně serializovatelné. Aby bylo možné zachovat stav uživatelského rozhraní, jako jsou například rozbalené uzly ovládacího prvku stromového zobrazení, musí aplikace používat vlastní kód pro modelování chování uživatelského rozhraní jako serializovatelnýho stavu aplikace.

Kam zachovat stav

Pro trvalý stav existují společná umístění:

Úložiště na straně serveru

V případě trvalé trvalosti dat, která zahrnuje více uživatelů a zařízení, může aplikace používat úložiště na straně serveru. Vaše možnosti jsou:

  • Blob Storage
  • Úložiště hodnot klíčů
  • Relační databáze
  • Table Storage

Po uložení dat se stav uživatele zachová a bude dostupný v jakémkoli novém okruhu.

Další informace o možnostech Azure Data Storage najdete v následujících tématech:

URL

Pro přechodná data představující stav navigace modelujte data jako součást adresy URL. Mezi příklady stavu uživatele v adrese URL patří:

  • ID zobrazené entity
  • Aktuální číslo stránky v mřížce na stránkovaném webu

Obsah panelu Adresa prohlížeče se zachová:

  • Pokud uživatel ručně znovu načte stránku.
  • Pokud dojde k nedostupnosti webového serveru a uživatel je nucen znovu načíst stránku, aby se mohl připojit k jinému serveru.

Informace o definování vzorů adres URL pomocí @page direktivy naleznete v tématu ASP.NET Core Blazor úsek .

Úložiště prohlížeče

Pro přechodná data, která uživatel aktivně vytváří, se běžně používané umístění úložiště nachází v prohlížeči localStorage a sessionStorage kolekcích:

  • localStorage je vymezen v okně prohlížeče. Pokud uživatel stránku znovu načte nebo zavře a znovu otevře prohlížeč, stav přetrvává. Pokud uživatel otevře více karet prohlížeče, stav se sdílí napříč kartami. Data přetrvají do localStorage doby, než je explicitně zrušeno.
  • sessionStorage je vymezen na kartu prohlížeče. Pokud uživatel znovu načte kartu, stav se přetrvá. Pokud uživatel zavře kartu nebo prohlížeč, dojde ke ztrátě stavu. Pokud uživatel otevře více karet prohlížeče, každá karta má svou vlastní nezávislou verzi dat.

Obecně sessionStorage je bezpečnější použít. sessionStorage Vyhněte se riziku, že uživatel otevře více karet a narazí na následující:

  • Chyby v úložišti stavů napříč kartami.
  • Matoucí chování, když karta Přepisuje stav jiných karet.

localStorage je lepší volbou, pokud aplikace musí zachovat stav v rámci zavírání a znovu otevřít prohlížeč.

Upozornění pro použití úložiště prohlížeče:

  • Podobně jako při použití databáze na straně serveru je načítání a ukládání dat asynchronní.
  • Na rozdíl od databáze na straně serveru není úložiště během předgenerování k dispozici, protože požadovaná stránka v prohlížeči neexistuje během fáze předvykreslování.
  • Storage o několika kilobajtech dat je pro aplikace vhodné zachovat Blazor Server . Po několika kilobajtech je potřeba vzít v úvahu dopad na výkon, protože data se načítají a ukládají v síti.
  • Uživatelé můžou data zobrazit nebo s nimi manipulovat. ochrana dat ASP.NET Core může riziko zmírnit. například ASP.NET Core chráněný prohlížeč Storage používá ASP.NET Core ochranu dat.

balíčky NuGet třetích stran poskytují rozhraní api pro práci s localStorage a sessionStorage . je vhodné zvážit výběr balíčku, který transparentně používá ASP.NET Core ochranu dat. Ochrana dat šifruje uložená data a snižuje potenciální riziko manipulace s uloženými daty. Pokud jsou data serializovaná do formátu JSON uložená v prostém textu, uživatelé můžou data zobrazit pomocí vývojářských nástrojů pro prohlížeč a také upravovat uložená data. Zabezpečení dat se vždy nejedná o problém, protože data můžou být triviální v podstatě. Například čtení nebo úprava uložené barvy prvku uživatelského rozhraní není významným bezpečnostním rizikem pro uživatele nebo organizaci. Neumožňuje uživatelům kontrolovat citlivá data a manipulovat s nimi.

chráněný prohlížeč Storage experimentální balíček NuGet

ASP.NET Core chráněný prohlížeč Storage využívá ASP.NET Core ochrany dat pro localStorage a sessionStorage .

Upozornění

Microsoft.AspNetCore.ProtectedBrowserStorage je nepodporovaný experimentální balíček, který není určený pro použití v produkčním prostředí.

balíček je dostupný jenom v aplikacích ASP.NET Core 3,1 Blazor Server .

Konfigurace

  1. Přidejte odkaz na balíček do Microsoft.AspNetCore.ProtectedBrowserStorage .

  2. Do tohoto Pages/_Host.cshtml souboru přidejte do uzavírací značky tento skript </body> :

    <script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>
    
  3. V Startup.ConfigureServices volejte volání metody AddProtectedBrowserStorage Add localStorage a sessionStorage Services do kolekce služeb:

    services.AddProtectedBrowserStorage();
    

Ukládání a načítání dat v rámci součásti

V každé součásti, která vyžaduje načtení nebo uložení dat do úložiště prohlížeče, použijte @inject direktivu pro vložení instance některého z následujících prvků:

  • ProtectedLocalStorage
  • ProtectedSessionStorage

Volba závisí na tom, jaké umístění úložiště prohlížeče chcete použít. V následujícím příkladu sessionStorage se používá:

@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore

@usingPříkaz lze umístit do _Imports.razor souboru místo v součásti. Použití _Imports.razor souboru zpřístupňuje obor názvů pro větší segmenty aplikace nebo celé aplikace.

Chcete-li zachovat currentCount hodnotu v Counter součásti aplikace na základě Blazor Server šablony projektu, upravte IncrementCount metodu, kterou chcete použít ProtectedSessionStore.SetAsync :

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

Ve větších a realističtějších aplikacích je ukládání jednotlivých polí nepravděpodobné. Pro aplikace je pravděpodobnější ukládání celých objektů modelu, které zahrnují komplexní stav. ProtectedSessionStore automaticky serializace a deserializace dat JSON.

V předchozím příkladu kódu currentCount se data ukládají jako sessionStorage['count'] v prohlížeči uživatele. data nejsou uložená v prostém textu, ale místo toho jsou chráněná pomocí ASP.NET Core ochrany dat. Zašifrovaná data lze zkontrolovat, pokud sessionStorage['count'] je vyhodnocena v konzole pro vývojáře v prohlížeči.

Chcete-li obnovit currentCount data, pokud se uživatel vrátí do Counter komponenty později, včetně toho, jestli se nachází na zcela novém okruhu, použijte ProtectedSessionStore.GetAsync :

protected override async Task OnInitializedAsync()
{
    currentCount = await ProtectedSessionStore.GetAsync<int>("count");
}

Pokud parametry komponenty obsahují stav navigace, zavolejte ProtectedSessionStore.GetAsync a přiřaďte výsledek v OnParametersSetAsync , nikoli OnInitializedAsync . OnInitializedAsync se volá pouze jednou při prvním vytvoření instance komponenty. OnInitializedAsync není voláno později, pokud uživatel přejde na jinou adresu URL a zůstane na stejné stránce. Další informace naleznete v tématu RazorASP.NET Core životní cyklus komponenty.

Upozornění

Příklady v této části fungují pouze v případě, že server nemá povolené předvykreslování. Pokud je povoleno předvykreslování, je vygenerována chyba s vysvětlením, že volání interoperability JavaScriptu nelze vydat, protože součást je předem vykreslena.

Buď zakažte předvykreslování nebo přidejte další kód pro práci s předvykreslováním. Další informace o psaní kódu, který funguje s předvykreslováním, najdete v části popisovač předvykreslování .

Zpracování stavu načítání

Vzhledem k tomu, že je úložiště prohlížeče přístupné asynchronně přes síťové připojení, je vždy časový interval před načtením dat a k dispozici pro komponentu. Nejlepších výsledků dosáhnete, když při načítání vykreslíte zprávu o stavu načítání, místo aby se zobrazila prázdná nebo výchozí data.

Jedním z těchto způsobů je sledovat, jestli data jsou null , což znamená, že se data pořád načítají. Ve výchozí Counter součásti je počet uchováván v int . Převést na typ s currentCount možnou hodnotou null přidáním otazníku ( ? ) do typu ( int ):

private int? currentCount;

Namísto nepodmíněného zobrazení Increment hodnoty a tlačítka vyberte možnost zobrazit tyto prvky pouze v případě, že jsou data načtena:

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

Zpracovat předvykreslování

Během předvykreslování:

  • Interaktivní připojení k prohlížeči uživatele neexistuje.
  • Prohlížeč ještě nemá stránku, ve které může spustit kód jazyka JavaScript.

localStorage nebo sessionStorage nejsou během předvykreslování k dispozici. Pokud se komponenta pokusí pracovat s úložištěm, je vygenerována chyba s vysvětlením, že volání interoperability JavaScriptu nelze vydat, protože součást je předem vykreslena.

Jedním ze způsobů, jak chybu vyřešit, je zakázat předvykreslování. To je obvykle nejlepší volba, pokud aplikace využívá úložiště založené na prohlížeči. Předvykreslování přináší složitost a nevýhoduje aplikaci, protože aplikace nemůže využít žádný užitečný obsah do té doby, než localStorage sessionStorage je k dispozici.

Chcete-li zakázat předvykreslování, otevřete Pages/_Host.cshtml soubor a změňte render-mode atribut pomocníka značek komponenty na Server :

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

Předvykreslování může být užitečné pro jiné stránky, které nepoužívají localStorage nebo sessionStorage . Chcete-li zachovat předvykreslování, odložte operaci načítání, dokud není prohlížeč připojen k okruhu. Následuje příklad uložení hodnoty čítače:

@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;
    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);
    }
}

Rozložte zachování stavu na společné místo.

Pokud mnoho komponent spoléhá na úložiště založené na prohlížeči, často se kód zprostředkovatele stavu znovu implementuje vytvořením duplicit kódu. Jednou z možností, jak zabránit duplikaci kódu, je vytvoření nadřazené komponenty poskytovatele stavu , která zapouzdřuje logiku poskytovatele stavu. Podřízené komponenty mohou pracovat s trvalými daty bez ohledu na mechanismus trvalosti stavu.

V následujícím příkladu CounterStateProvider komponenty jsou data čítače trvalá sessionStorage :

@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 OnInitializedAsync()
    {
        CurrentCount = await ProtectedSessionStore.GetAsync<int>("count");
        isLoaded = true;
    }

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

CounterStateProviderKomponenta zpracovává fázi načítání tím, že nevykresluje svůj podřízený obsah, dokud není načítání dokončeno.

Chcete-li použít CounterStateProvider komponentu, zabalte instanci součásti kolem jakékoli jiné komponenty, která vyžaduje přístup ke stavu čítače. Chcete-li zpřístupnit stav pro všechny komponenty v aplikaci, zabalte CounterStateProvider komponentu kolem komponenty Router v App součásti ( App.razor ):

<CounterStateProvider>
    <Router AppAssembly="@typeof(Program).Assembly">
        ...
    </Router>
</CounterStateProvider>

Zabalené komponenty obdrží a můžou upravovat trvalý stav čítače. CounterVzor implementuje následující součást:

@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()
    {
        CounterStateProvider.CurrentCount++;
        await CounterStateProvider.SaveChangesAsync();
    }
}

Předchozí komponenta není nutná k interakci s ProtectedBrowserStorage , ani nefunguje se fází "načítání".

Chcete-li se vypořádat s předem popsaným způsobem, CounterStateProvider lze upravit tak, aby všechny součásti, které spotřebovávají data čítače, pracovaly automaticky s předem vykreslením. Další informace najdete v části zpracování předvykreslování .

Obecně se doporučuje model nadřazené komponenty zprostředkovatele stavu :

  • Pro využívání stavu napříč mnoha komponentami.
  • Pokud existuje pouze jeden objekt stavu nejvyšší úrovně, který má být zachován.

Chcete-li zachovat mnoho různých stavových objektů a využívat různé podmnožiny objektů na různých místech, je lepší vyhnout se zachování stavu globálně.

Služba kontejneru stavu v paměti

Vnořené komponenty obvykle vážou data pomocí zřetězovaných vazeb, jak je popsáno v části BlazorASP.NET Core datová vazba . Vnořené a nezatížené komponenty mohou sdílet přístup k datům pomocí registrovaného kontejneru stavu v paměti. Třída kontejneru vlastního stavu může pomocí přiřaditelného objektu upozornit komponenty v různých částech aplikace Action na změny stavu. V následujícím příkladu:

  • Dvojice komponent používá kontejner stavu ke sledování vlastnosti.
  • Jedna komponenta v následujícím příkladu je vnořená v druhé komponentě, ale vnoření není nutné, aby tento přístup fungoval.

StateContainer.cs:

using System;

public class StateContainer
{
    private string savedString;

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

    public event Action OnChange;

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

V Program.cs souboru ( Blazor WebAssembly ):

builder.Services.AddSingleton<StateContainer>();

V Program.cs ( ) ve ASP.NET Core Blazor Server 6.0 nebo novějším:

builder.Services.AddScoped<StateContainer>();

V Startup.ConfigureServices ( ) ve Blazor Server verzích ASP.NET Core verze starší než 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;
    }
}

Pages/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;
    }
}

Předchozí komponenty implementují a delegáti se odhlásí v metodách, které jsou volány architekturou při uvolnění IDisposable OnChange Dispose komponent. Další informace naleznete v tématu RazorASP.NET Core životní cyklus komponenty.