správa základního Blazor stavu ASP.NET

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Upozorňující

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v tématu .NET a .NET Core Zásady podpory. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Tento článek popisuje běžné přístupy k údržbě dat (stavu) uživatele při používání aplikace a napříč relacemi prohlížeče.

Poznámka:

Příklady kódu v tomto článku přijímají referenční typy s možnou hodnotou null (NRT) a statickou analýzu stavu null-stav kompilátoru .NET, které jsou podporovány v ASP.NET Core v .NET 6 nebo novější. Při cílení na ASP.NET Core 5.0 nebo starší odeberte označení typu null z? typů v příkladech článku.

Udržovat stav uživatele

Na straně Blazor serveru je stavová aplikační architektura. Ve většině případů aplikace udržuje připojení k serveru. Stav uživatele se uchovává v paměti serveru v okruhu.

Mezi příklady stavu uživatele uchovávaného v okruhu patří:

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

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

Pokud uživatel dojde k dočasné ztrátě síťového připojení, Blazor pokusí se uživatele znovu připojit k původnímu okruhu 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 trvale zachovat odpojený okruh. Server musí po vypršení časového limitu uvolnit odpojený okruh nebo když je server pod tlakem paměti.
  • V prostředích nasazení s více servery s vyrovnáváním zatížení může dojít k selhání jednotlivých serverů nebo k jejich automatickému odebrání, pokud už není potřeba zpracovat celkový objem požadavků. Původní požadavky na zpracování serveru pro uživatele můžou být nedostupné, když se uživatel pokusí znovu připojit.
  • Uživatel může zavřít a znovu otevřít prohlížeč nebo znovu načíst stránku, která odebere jakýkoli stav uložený v paměti prohlížeče. Například hodnoty proměnných Jazyka JavaScript nastavené prostřednictvím volání zprostředkovatele komunikace JavaScriptu se ztratí.

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

Zachování stavu napříč okruhy

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

Aby se zachoval stav napříč okruhy, musí aplikace uchovávat 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 provést kroky pro implementaci stavové trvalosti dat.

Trvalost dat se obvykle vyžaduje jenom pro vysoce hodnotný stav, kdy uživatelé vynaložili úsilí na vytvoření. V následujících příkladech zachování stavu šetří čas nebo pomůcky v komerčních aktivitách:

  • Vícekrokové webové formuláře: Je časově náročné, aby uživatel 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 v tomto scénáři ztratí stav, pokud přejde mimo formulář a vrátí se později.
  • Nákupní košíky: Všechny komerčně důležité součásti aplikace, které představují potenciální výnosy, je možné zachovat. Uživatel, který ztratí svůj stav, a tedy nákupní košík, může při pozdějším návratu na web zakoupit méně produktů nebo služeb.

Aplikace může zachovat pouze stav aplikace. UI nelze zachovat, například instance komponent a jejich vykreslovací stromy. Komponenty a vykreslovací stromy nejsou obecně serializovatelné. Pokud chcete zachovat stav uživatelského rozhraní, například 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

Pro zachování stavu existují běžná umístění:

Úložiště na straně serveru

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

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

Po uložení dat se stav uživatele zachová a zpřístupní v jakémkoli novém okruhu.

Další informace o možnostech úložiště dat Azure najdete v následujících tématech:

Adresa URL

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

  • ID zobrazené entity.
  • Číslo aktuální stránky v stránkované mřížce

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

  • Pokud uživatel stránku znovu načte ručně.
  • Pokud se webový server stane nedostupným 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 najdete v tématu ASP.NET Blazor Základní směrování a navigace.

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

U přechodných dat, která uživatel aktivně vytváří, je běžně používané umístění úložiště umístěním prohlížeče localStorage a sessionStorage kolekcí:

  • localStorage je vymezený na okno prohlížeče. Pokud uživatel stránku znovu načte nebo ji 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ávají až localStorage do explicitního vymazání.
  • sessionStorage je vymezený na kartu prohlížeče. Pokud uživatel kartu znovu načte, 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 je bezpečnější používat. sessionStorage zabraňuje riziku, že uživatel otevře více karet a narazí na následující:

  • Chyby ve stavové úložišti na kartách
  • Matoucí chování při přepsání stavu jiných karet tabulátoru

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

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

  • Podobně jako použití databáze na straně serveru jsou načítání a ukládání dat asynchronní.
  • Požadovaná stránka v prohlížeči neexistuje během předrenderingu, takže během předkreslování není místní úložiště dostupné.
  • Úložiště několika kilobajtů dat je vhodné zachovat pro aplikace na straně Blazor serveru. Kromě několika kilobajtů je potřeba zvážit dopad na výkon, protože se data načítají a ukládají v síti.
  • Uživatelé můžou data zobrazit nebo zfalšovat. ASP.NET Základní ochrana dat může riziko zmírnit. Například ASP.NET Core Protected Browser Storage používá ASP.NET Základní ochrana 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 Data Protection. Ochrana dat šifruje uložená data a snižuje potenciální riziko manipulace s uloženými daty. Pokud jsou serializovaná data JSON uložená ve formátu prostého textu, můžou uživatelé zobrazit data pomocí vývojářských nástrojů prohlížeče a také upravit uložená data. Zabezpečení triviálních dat není problém. 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. Vyhněte se tomu, aby uživatelé mohli kontrolovat nebo manipulovat s citlivými daty.

úložiště chráněného prohlížečem ASP.NET Core

ASP.NET Core Protected Browser Storage využívá ASP.NET základní ochranu dat pro localStorage a sessionStorage.

Poznámka:

Chráněné úložiště prohlížeče spoléhá na ASP.NET Core Data Protection a podporuje se jenom pro aplikace na straně Blazor serveru.

Upozorňující

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

Balíček je k dispozici pouze pro použití v aplikacích ASP.NET Core 3.1.

Konfigurace

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

    Poznámka:

    Pokyny k přidávání balíčků do aplikací .NET najdete v článcích v části Instalace a správa balíčků na webu Pracovní postup používání balíčků (dokumentace k NuGetu). Ověřte správné verze balíčků na NuGet.org.

  2. _Host.cshtml Do souboru přidejte do koncové </body> značky následující skript:

    <script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>
    
  3. AddProtectedBrowserStorage Volání Startup.ConfigureServicespro přidání localStorage a sessionStorage služby do kolekce služeb:

    services.AddProtectedBrowserStorage();
    

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

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

  • ProtectedLocalStorage
  • ProtectedSessionStorage

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

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

Direktivu @using lze umístit do souboru aplikace _Imports.razor namísto komponenty. _Imports.razor Použití souboru zpřístupňuje obor názvů větším segmentům aplikace nebo celé aplikace.

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

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

Ve větších, realističtějších aplikacích je úložiště jednotlivých polí nepravděpodobné. 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í složitých stavových objektů.

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

Chcete-li obnovit currentCount data, pokud se uživatel později vrátí do Counter komponenty, včetně toho, zda je uživatel v 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;
}
protected override async Task OnInitializedAsync()
{
    currentCount = await ProtectedSessionStore.GetAsync<int>("count");
}

Pokud parametry komponenty zahrnují stav navigace, zavolejte ProtectedSessionStore.GetAsync a přiřaďte výsledekOnParametersSetAsync,null nikoli OnInitializedAsync. OnInitializedAsync je volána pouze jednou při první vytvoření instance komponenty. OnInitializedAsync není znovu volána později, pokud uživatel přejde na jinou adresu URL a zůstane na stejné stránce. Další informace najdete v tématu Životní cyklus komponent ASP.NET Core Razor.

Upozorňující

Příklady v této části fungují jenom v případě, že server nemá povolené předběžné nastavení. Při povoleném předběžném generování se vygeneruje chyba s vysvětlením, že volání interop JavaScriptu nelze vydat, protože komponenta je předem vygenerována.

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

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

Vzhledem k tomu, že k úložišti prohlížeče se přistupuje asynchronně přes síťové připojení, je vždy čas před načtením dat a dostupným pro komponentu. Nejlepších výsledků dosáhnete tak, že během načítání vykreslíte zprávu místo zobrazení prázdných nebo výchozích dat.

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

private int? currentCount;

Místo nepodmíněného zobrazení počtu 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>
}

Zpracování předrenderingu

Během předkreslování:

  • Interaktivní připojení k prohlížeči uživatele neexistuje.
  • Prohlížeč zatím nemá stránku, ve které může spustit javascriptový kód.

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

Jedním ze způsobů, jak tuto chybu vyřešit, je zakázat předrendering. To je obvykle nejlepší volbou, pokud aplikace využívá velké využití úložiště založeného na prohlížeči. Předrenderování zvyšuje složitost a nemá pro aplikaci výhodu, protože aplikace nemůže předem vyřídit žádný užitečný obsah, dokud localStorage nebude dostupný nebo sessionStorage nebude k dispozici.

Chcete-li zakázat předběžné vykreslování, označte režim vykreslování s parametrem prerender nastaveným na false komponentu nejvyšší úrovně v hierarchii komponent aplikace, která není kořenovou komponentou.

Poznámka:

Interaktivní vytvoření kořenové komponenty, například App komponenty, není podporováno. Proto není možné předřazování přímo zakázat komponentou App .

U aplikací založených na Blazor Web App šabloně projektu je prerendering obvykle zakázán, pokud se komponenta Routes používá v App dané komponentě (Components/App.razor):

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

Zakažte také předběžné předkreslování pro komponentu HeadOutlet :

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

Další informace najdete v tématu ASP.NET režimy vykreslování coreBlazor.

Chcete-li zakázat předkreslování, otevřete _Host.cshtml soubor a změňte render-mode atribut pomocné rutiny značky komponenty na Server:

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

Pokud je předřažení zakázané, je předkreslování <head> obsahu zakázané.

Předkreslování může být užitečné pro jiné stránky, které nepoužívají localStorage nebo sessionStorage. Pokud chcete zachovat předrendering, odložte operaci načítání, dokud nebude prohlížeč připojený k okruhu. Následuje příklad pro 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);
    }
}
@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);
    }
}

Vyvádět zachování stavu do společného umístění

Pokud mnoho komponent spoléhá na úložiště založené na prohlížeči, implementace kódu zprostředkovatele stavu mnohokrát vytvoří duplikaci kódu. Jednou z možností, jak se vyhnout 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 CounterStateProvider komponenty se data čítačů uchovávají na 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);
    }
}
@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);
    }
}

Poznámka:

Další informace o RenderFragmentkomponentách ASP.NET CoreRazor.

Komponenta CounterStateProvider zpracovává fázi načítání tak, že nevykresluje podřízený obsah, dokud se načtení stavu nedokončí.

Pokud chcete, aby byl stav přístupný pro všechny komponenty v aplikaci, zabalte CounterStateProvider komponentu kolem Router komponenty (<Router>...</Router>) pomocí Routes globálního interaktivního vykreslování na straně serveru (interaktivní SSR).

V komponentě App (Components/App.razor):

<Routes @rendermode="InteractiveServer" />

V komponentě Routes (Components/Routes.razor):

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

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

Poznámka:

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

Zabalené komponenty přijímají a mohou upravit trvalý stav čítače. Counter Následující 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()
    {
        if (CounterStateProvider is not null)
        {
            CounterStateProvider.CurrentCount++;
            await CounterStateProvider.SaveChangesAsync();
        }
    }
}

Předchozí komponenta není nutná k interakci ProtectedBrowserStorageani s fází načítání.

Chcete-li řešit předběžné úpravy, jak je popsáno výše, je možné upravit tak, CounterStateProvider aby všechny komponenty, které spotřebovávají data čítače, automaticky fungovaly s předrenderingem. Další informace najdete v části Popisovač předrenderingu .

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

  • Pokud chcete využívat stav napříč mnoha komponentami.
  • Pokud existuje jen jeden objekt stavu nejvyšší úrovně, který se má zachovat.

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

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

Mezi příklady stavu uživatele uchovávaného v paměti prohlížeče patří:

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

Poznámka:

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

Zachování stavu napříč relacemi prohlížeče

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

Aby se zachoval stav napříč relacemi prohlížeče, musí aplikace uchovávat data v jiném umístění úložiště než v paměti prohlížeče. Trvalost stavu není automatická. Při vývoji aplikace musíte provést kroky pro implementaci stavové trvalosti dat.

Trvalost dat se obvykle vyžaduje jenom pro vysoce hodnotný stav, kdy uživatelé vynaložili úsilí na vytvoření. V následujících příkladech zachování stavu šetří čas nebo pomůcky v komerčních aktivitách:

  • Vícekrokové webové formuláře: Je časově náročné, aby uživatel 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 v tomto scénáři ztratí stav, pokud přejde mimo formulář a vrátí se později.
  • Nákupní košíky: Všechny komerčně důležité součásti aplikace, které představují potenciální výnosy, je možné zachovat. Uživatel, který ztratí svůj stav, a tedy nákupní košík, může při pozdějším návratu na web zakoupit méně produktů nebo služeb.

Aplikace může zachovat pouze stav aplikace. UI nelze zachovat, například instance komponent a jejich vykreslovací stromy. Komponenty a vykreslovací stromy nejsou obecně serializovatelné. Pokud chcete zachovat stav uživatelského rozhraní, například 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

Pro zachování stavu existují běžná umístění:

Úložiště na straně serveru

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

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

Po uložení dat se stav uživatele zachová a zpřístupní v jakékoli nové relaci prohlížeče.

Vzhledem k tomu, že Blazor WebAssembly 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 databáze. Aplikace Blazor WebAssembly jsou zabezpečené stejným způsobem jako jednostránkové aplikace (SPA). Aplikace obvykle ověřuje uživatele prostřednictvím OAuth/OpenID Connect (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 Blazor WebAssembly aplikací a službou úložiště nebo databází. Aplikace Blazor WebAssembly 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 úložiště dat Azure najdete v následujících tématech:

Adresa URL

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

  • ID zobrazené entity.
  • Číslo aktuální stránky v stránkované mřížce

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

Informace o definování vzorů adres URL pomocí @page direktivy najdete v tématu ASP.NET Blazor Základní směrování a navigace.

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

U přechodných dat, která uživatel aktivně vytváří, je běžně používané umístění úložiště umístěním prohlížeče localStorage a sessionStorage kolekcí:

  • localStorage je vymezený na okno prohlížeče. Pokud uživatel stránku znovu načte nebo ji 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ávají až localStorage do explicitního vymazání.
  • sessionStorage je vymezený na kartu prohlížeče. Pokud uživatel kartu znovu načte, 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 dají se používat v Blazor WebAssembly aplikacích, ale jenom napsáním vlastního kódu nebo použitím balíčku třetí strany.

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

  • Chyby ve stavové úložišti na kartách
  • Matoucí chování při přepsání stavu jiných karet tabulátoru

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

Upozorňující

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 sváže data pomocí zřetězených vazeb, jak je popsáno v datové vazbě ASP.NET CoreBlazor. Vnořené a nenestované komponenty můžou sdílet přístup k datům pomocí registrovaného kontejneru stavu v paměti. Třída kontejneru vlastního stavu může použít přiřaditelné Action k oznamování komponent v různých částech aplikace změn stavu. V následujícím příkladu:

  • Dvojice komponent používá ke sledování vlastnosti kontejner stavu.
  • Jedna komponenta v následujícím příkladu je vnořená do druhé komponenty, ale pro tento přístup není vyžadováno vnoření.

Důležité

Příklad v této části ukazuje, jak vytvořit službu kontejneru stavu v paměti, zaregistrovat službu a používat ji v komponentách. Příklad neuchovává data bez dalšího vývoje. V případě trvalého úložiště dat musí kontejner stavu přijmout základní mechanismus úložiště, který přežije, když se vymaže paměť prohlížeče. Toho lze dosáhnout pomocí localStorage/sessionStorage nebo jiné 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();
}

Klientské aplikace (Program soubor):

builder.Services.AddSingleton<StateContainer>();

Serverové aplikace (Program soubor, ASP.NET Core v .NET 6 nebo novější):

builder.Services.AddScoped<StateContainer>();

Serverové aplikace (Startup.ConfigureServices z Startup.cs, ASP.NET Core 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;
    }
}

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í IDisposablea OnChange delegáti se odhlásí v Dispose metodách, které jsou volány rozhraním při odstranění komponent. Další informace najdete v tématu Životní cyklus komponent ASP.NET Core Razor.

Další přístupy

Při implementaci vlastního úložiště stavu je užitečným přístupem přijmout kaskádové hodnoty a parametry:

  • Pokud chcete využívat stav napříč mnoha komponentami.
  • Pokud existuje jen jeden objekt stavu nejvyšší úrovně, který se má zachovat.

Odstraňování potíží

Ve vlastní službě správy stavu musí zpětné volání vyvolané mimo Blazorkontext synchronizace zabalit logiku zpětného ComponentBase.InvokeAsync volání, aby se přesunula do kontextu synchronizace rendereru.

Pokud služba správy stavu nevolá StateHasChanged Blazorkontext synchronizace, vyvolá se následující chyba:

System.InvalidOperationException: Aktuální vlákno není přidruženo k Dispatcheru. Pomocí InvokeAsync() můžete při aktivaci vykreslování nebo stavu součásti přepnout spuštění na Dispečera.

Další informace a příklad řešení této chyby najdete v tématu ASP.NET vykreslování komponent CoreRazor.

Další materiály