Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
För tillfälliga data som användaren aktivt skapar är en vanlig lagringsplats webbläsarens localStorage och sessionStorage samlingar:
-
localStorageär begränsad till webbläsarens instans. Om användaren läser in sidan igen eller stänger och öppnar webbläsaren igen kvarstår tillståndet. Om användaren öppnar flera webbläsarflikar delas tillståndet mellan flikarna. Data sparas ilocalStoragetills datan uttryckligen tas bort.localStorage-data för ett dokument som läses in i en "privat" eller "inkognito"-session rensas när den sista "privata" fliken stängs. -
sessionStorageär begränsad till webbläsarfliken. Om användaren läser in fliken igen bevaras tillståndet. Om användaren stänger fliken eller webbläsaren går tillståndet förlorat. Om användaren öppnar flera webbläsarflikar har varje flik en egen oberoende version av data.
I allmänhet är sessionStorage säkrare att använda.
sessionStorage undviker risken att en användare öppnar flera flikar och stöter på följande:
- Problem med lagring av tillstånd över flikar.
- Förvirrande beteende när en flik skriver över tillståndet för andra flikar.
localStorage är det bättre valet om appen måste bevara tillståndet över stängning och återöppning av webbläsaren.
Varningar för att använda webbläsarlagring:
- På samma sätt som för en databas på serversidan är inläsning och sparande av data asynkrona.
- Den begärda sidan finns inte i webbläsaren under förinläsningen, så lokal lagring är inte tillgänglig under prerendering.
- Det är rimligt att lagra några kilobyte data för serverbaserade Blazor-appar. Utöver några kilobyte måste du tänka på prestandakonsekvenserna eftersom data läses in och sparas i nätverket.
- Användare kan visa eller manipulera data. ASP.NET Core Data Protection- kan minska risken. Till exempel använder ASP.NET Core Protected Browser Storage ASP.NET Core Data Protection.
NuGet-paket från tredje part tillhandahåller API:er för att arbeta med localStorage och sessionStorage. Det är värt att överväga att välja ett paket som transparent använder ASP.NET Core Data Protection. Dataskydd krypterar lagrade data och minskar den potentiella risken för manipulering av lagrade data. Om JSON-serialiserade data lagras i oformaterad text kan användarna se data med hjälp av webbläsarutvecklarverktyg och även ändra lagrade data. Att skydda triviala data är inget problem. Till exempel är läsning eller ändring av den lagrade färgen för ett användargränssnittselement inte en betydande säkerhetsrisk för användaren eller organisationen. Undvik att tillåta användare att inspektera eller manipulera känsliga data.
ASP.NET Core Skyddad Webbläsarlagring
ASP.NET Core Protected Browser Storage utnyttjar ASP.NET Core Data Protection för localStorage och sessionStorage.
Anmärkning
Protected Browser Storage förlitar sig på ASP.NET Core Data Protection och stöds endast för Blazor appar på serversidan.
Varning
Microsoft.AspNetCore.ProtectedBrowserStorage är ett experimentpaket som inte stöds och som inte är avsett för produktionsanvändning.
Paketet är endast tillgängligt för användning i ASP.NET Core 3.1-appar.
Konfiguration
Lägg till en paketreferens till
Microsoft.AspNetCore.ProtectedBrowserStorage.Anmärkning
Vägledning om hur du lägger till paket i .NET-appar finns i artiklarna under Installera och hantera paket i arbetsflödet för paketförbrukning (NuGet-dokumentation). Bekräfta rätt paketversioner på NuGet.org.
I filen
_Host.cshtmllägger du till följande skript i den avslutande</body>-taggen:<script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>I
Startup.ConfigureServicesanropar duAddProtectedBrowserStorageför att lägga tilllocalStorageochsessionStoragetjänster i tjänstsamlingen:services.AddProtectedBrowserStorage();
Spara och läsa in data i en komponent
Använd @inject-direktivet för att mata in en instans av något av följande i alla komponenter som kräver inläsning eller sparande av data till webbläsarlagring:
ProtectedLocalStorageProtectedSessionStorage
Valet beror på vilken webbläsarlagringsplats du vill använda. I följande exempel används sessionStorage:
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
@using-direktivet kan placeras i appens _Imports.razor-fil i stället för i komponenten. Med hjälp av _Imports.razor-filen blir namnområdet tillgängligt för större segment i appen eller hela appen.
Om du vill spara värdet för currentCount i Counter-komponenten i en app baserat på Blazor-projektmallenändrar du metoden IncrementCount för att använda ProtectedSessionStore.SetAsync:
private async Task IncrementCount()
{
currentCount++;
await ProtectedSessionStore.SetAsync("count", currentCount);
}
I större, mer realistiska appar är lagring av enskilda fält ett osannolikt scenario. Appar är mer benägna att lagra hela modellobjekt som innehåller komplext tillstånd.
ProtectedSessionStore serialiserar och deserialiserar JSON-data automatiskt för att lagra komplexa tillståndsobjekt.
I föregående kodexempel lagras currentCount data som sessionStorage['count'] i användarens webbläsare. Data lagras inte i oformaterad text utan skyddas i stället med hjälp av ASP.NET Core Data Protection. Krypterade data kan kontrolleras om sessionStorage['count'] utvärderas i webbläsarens utvecklarkonsol.
Om du vill återställa currentCount data om användaren återgår till Counter komponenten senare, inklusive om användaren är på en ny krets, använder du 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");
}
Om komponentens parametrar innehåller navigeringstillstånd, anropar du ProtectedSessionStore.GetAsync och tilldelar ett icke-null-resulterande värde i OnParametersSetAsync, inte OnInitializedAsync.
OnInitializedAsync anropas bara en gång när komponenten först instansieras.
OnInitializedAsync anropas inte igen senare om användaren navigerar till en annan URL medan den finns kvar på samma sida. Mer information finns i ASP.NET Core Razor-komponentens livscykel.
Varning
Exemplen i det här avsnittet fungerar bara om servern inte har förrendering aktiverat. När förrendering är aktiverad genereras ett fel som förklarar att JavaScript-interoperabilitetsanrop inte kan utfärdas eftersom komponenten förrenderas.
Inaktivera antingen prerendering eller lägg till ytterligare kod för att arbeta med prerendering. Mer information om hur du skriver kod som fungerar med prerendering finns i avsnittet Handle prerendering .
Hantera laddningstillståndet
Eftersom webbläsarlagring används asynkront via en nätverksanslutning finns det alltid en tidsperiod innan data läses in och är tillgängliga för en komponent. För bästa resultat kan du återge ett meddelande medan inläsning pågår i stället för att visa tomma data eller standarddata.
En metod är att spåra om datan är null, vilket innebär att datan fortfarande läses in. I standardkomponenten Counter lagras antalet i en int.
Gör currentCount nullbar genom att lägga till ett frågetecken (?) i typen (int):
private int? currentCount;
Istället för att villkorslöst visa antalet och Increment-knappen, ska du visa dessa element endast om datan läses in genom att kontrollera HasValue:
@if (currentCount.HasValue)
{
<p>Current count: <strong>@currentCount</strong></p>
<button @onclick="IncrementCount">Increment</button>
}
else
{
<p>Loading...</p>
}
Hantera prerendering
Under förberäknande:
- Det finns ingen interaktiv anslutning till användarens webbläsare.
- Webbläsaren har ännu ingen sida där den kan köra JavaScript-kod.
localStorage eller sessionStorage är inte tillgängliga under förberedande rendering. Om komponenten försöker interagera med lagring genereras ett fel som förklarar att JavaScript-interop-anrop inte kan utfärdas eftersom komponenten förinstalleras.
Ett sätt att lösa felet är att inaktivera prerendering. Detta är vanligtvis det bästa valet om appen använder webbläsarbaserad lagring. Prerendering lägger till ytterligare komplexitet och gynnar inte appen eftersom appen inte kan rendera i förväg något användbart innehåll förrän localStorage eller sessionStorage är tillgängliga.
Om du vill inaktivera förrendering anger du återgivningsläget med parametern prerender inställd på false på den högsta komponenten i appens komponenthierarki som inte är en rotkomponent.
Anmärkning
Det går inte att göra en rotkomponent interaktiv, till exempel komponenten App. Därför kan prerendering inte inaktiveras direkt av komponenten App.
För appar som baseras på Blazor Web App projektmall inaktiveras vanligtvis prerendering där den Routes komponenten används i komponenten App (Components/App.razor):
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
Inaktivera även prerendering för den HeadOutlet komponenten:
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
Mer information finns i Prerender ASP.NET Core-komponenterRazor.
Om du vill inaktivera förrendering öppnar du filen _Host.cshtml och ändrar attributet render-mode för -komponenttaggen till Server:
<component type="typeof(App)" render-mode="Server" />
När förrendering är inaktiverat, är förrendering av <head> innehåll inaktiverat.
Prerendering kan vara användbart för andra sidor som inte använder localStorage eller sessionStorage. Om du vill behålla förrendering skjuter du upp inläsningsåtgärden tills webbläsaren är ansluten till kretsen. Följande är ett exempel på hur du lagrar ett räknarvärde:
@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);
}
}
Bryta ut tillståndshantering till en gemensam leverantör
Om många komponenter förlitar sig på webbläsarbaserad lagring skapar implementering av tillståndsproviderkod många gånger kodduplicering. Ett alternativ för att undvika kodduplicering är att skapa en överordnad komponent för tillståndsprovidern som kapslar in tillståndsproviderlogik. Barnkomponenter kan arbeta med beständiga data utan hänsyn till mekanismen för beständigt tillstånd.
I följande exempel på en CounterStateProvider-komponent sparas räknardata i sessionStorage, och den hanterar inläsningsfasen genom att inte återge sitt underordnade innehåll förrän inläsningen av tillståndet har slutförts.
Komponenten CounterStateProvider hanterar prerendering genom att inte läsa in tillstånd förrän efter komponentrendering i OnAfterRenderAsync livscykelmetod, som inte körs under prerendering.
Metoden i det här avsnittet kan inte utlösa återrendering av flera prenumerationskomponenter på samma sida. Om en prenumererad komponent ändrar tillståndet, renderar den igen och kan visa det uppdaterade tillståndet, men en annan komponent på samma sida som visar detta tillstånd visar föråldrad data tills den själv renderas om nästa gång. Därför passar metoden som beskrivs i det här avsnittet bäst för att använda tillstånd i en enskild komponent på sidan.
CounterStateProvider.razor:
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
@if (isLoaded)
{
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
}
else
{
<p>Loading...</p>
}
@code {
private bool isLoaded;
[Parameter]
public RenderFragment? ChildContent { get; set; }
public int CurrentCount { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
isLoaded = true;
await LoadStateAsync();
StateHasChanged();
}
}
private async Task LoadStateAsync()
{
var result = await ProtectedSessionStore.GetAsync<int>("count");
CurrentCount = result.Success ? result.Value : 0;
isLoaded = true;
}
public async Task IncrementCount()
{
CurrentCount++;
await ProtectedSessionStore.SetAsync("count", CurrentCount);
}
}
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
@if (isLoaded)
{
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
}
else
{
<p>Loading...</p>
}
@code {
private bool isLoaded;
[Parameter]
public RenderFragment ChildContent { get; set; }
public int CurrentCount { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
isLoaded = true;
await LoadStateAsync();
StateHasChanged();
}
}
private async Task LoadStateAsync()
{
CurrentCount = await ProtectedSessionStore.GetAsync<int>("count");
isLoaded = true;
}
public async Task IncrementCount()
{
CurrentCount++;
await ProtectedSessionStore.SetAsync("count", CurrentCount);
}
}
Anmärkning
Mer information om RenderFragmentfinns i ASP.NET Core Razor-komponenter.
Om du vill göra tillståndet tillgängligt för alla komponenter i en app omsluter du CounterStateProvider-komponenten runt Router (<Router>...</Router>) i komponenten Routes med global interaktiv återgivning på serversidan (interaktiv SSR).
I komponenten App (Components/App.razor):
<Routes @rendermode="InteractiveServer" />
I komponenten Routes (Components/Routes.razor):
Om du vill använda komponenten CounterStateProvider omsluter du en instans av komponenten runt alla andra komponenter som kräver åtkomst till räknartillståndet. Om du vill göra tillståndet tillgängligt för alla komponenter i en app omsluter du CounterStateProvider-komponenten runt Router i komponenten App (App.razor):
<CounterStateProvider>
<Router ...>
...
</Router>
</CounterStateProvider>
Anmärkning
Med versionen av .NET 5.0.1 och för ytterligare 5.x-versioner innehåller komponenten Router parametern PreferExactMatches inställd på @true. Mer information finns i Migrera från ASP.NET Core 3.1 till .NET 5.
Inslutna komponenter tar emot och kan ändra det sparade räknartillståndet. Följande Counter komponent implementerar mönstret:
@page "/counter"
<p>Current count: <strong>@CounterStateProvider?.CurrentCount</strong></p>
<button @onclick="IncrementCount">Increment</button>
@code {
[CascadingParameter]
private CounterStateProvider? CounterStateProvider { get; set; }
private async Task IncrementCount()
{
if (CounterStateProvider is not null)
{
await CounterStateProvider.IncrementCount();
}
}
}
Den föregående komponenten är inte nödvändig för att interagera med ProtectedBrowserStorageoch den hanterar inte heller en "inläsningsfas".
Generellt rekommenderas mönstret för tillståndsprovider där är en överordnad komponent åt.
- Hantera tillståndet över många komponenter.
- Om det bara finns ett tillståndsobjekt på den översta nivån att bevara.
Om du vill bevara många olika tillståndsobjekt och använda olika delmängder av objekt på olika platser är det bättre att undvika att bevara tillståndet globalt.
ASP.NET Core