Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Für vorübergehende Daten, die der Benutzer aktiv erstellt, sind die localStorage- und sessionStorage-Sammlungen des Browsers ein häufig verwendeter Speicherort:
-
localStorageist auf die Instanz des Browsers beschränkt. Wenn der Benutzer die Seite neu lädt oder den Browser schließt und erneut öffnet, wird der Zustand beibehalten. Wenn der Benutzer mehrere Browserregisterkarten öffnet, wird der Zustand auf allen Registerkarten freigegeben. Daten bleiben inlocalStorageso lange beibehalten, bis sie explizit gelöscht werden. DielocalStorageDaten für ein Dokument, das in einer Sitzung im "privaten Modus" oder im "Inkognito" geladen wurde, werden gelöscht, wenn der letzte "private" Tab geschlossen wird. -
sessionStoragebezieht sich auf die Registerkarte des Browsers. Wenn der Benutzer die Registerkarte noch mal lädt, wird der Zustand beibehalten. Wenn der Benutzer die Registerkarte oder den Browser schließt, geht der Zustand verloren. Wenn der Benutzer mehrere Browserregisterkarten öffnet, hat jede Registerkarte eine eigene unabhängige Version der Daten.
Im Allgemeinen ist sessionStorage in der Anwendung sicherer.
sessionStorage verhindert, dass ein Benutzer mehrere Registerkarten öffnet und auf folgende Probleme stößt:
- Fehler in Zustandsspeicher über mehrere Registerkarten hinweg
- Verwirrendes Verhalten, wenn eine Registerkarte den Zustand anderer Registerkarten überschreibt
localStorage ist die bessere Wahl, wenn der Zustand der App auch nach dem Schließen und erneuten Öffnen des Browsers bestehen bleiben muss.
Einschränkungen bei der Verwendung des Browserspeichers:
- Ähnlich wie bei der Verwendung einer serverseitigen Datenbank erfolgt das Laden und Speichern von Daten asynchron.
- Die angeforderte Seite ist während des Vorrenderns nicht im Browser vorhanden, sodass der lokale Speicher während des Prozesses des Vorrenderns nicht verfügbar ist.
- Die Speicherung einiger Kilobyte-Daten ist für serverseitige Blazor-Apps angemessen. Bei mehr Kilobyte müssen Beeinträchtigungen der Leistung berücksichtigt werden, da die Daten über das Netzwerk geladen und gespeichert werden.
- Benutzer können die Daten anzeigen oder manipulieren. Die Datenschutzlösung von ASP.NET Core reduziert die damit verbundenen Risiken. Beispielsweise verwendet ASP.NET Core Protected Browser Storage ASP.NET Core-Datenschutz.
NuGet-Pakete von Drittanbietern stellen APIs für die Verwendung mit localStorage und sessionStorage bereit. Erwägen Sie die Auswahl eines Pakets, das die Datenschutzlösung von ASP.NET Core transparent verwendet. Die Datenschutzlösung verschlüsselt gespeicherte Daten und verringert das potenzielle Risiko, dass gespeicherte Daten manipuliert werden. Wenn JSON-serialisierte Daten als Nur-Text gespeichert werden, können Benutzer die Daten mithilfe von Browserentwicklertools anzeigen und auch die gespeicherten Daten ändern. Die Sicherung trivialer Daten ist kein Problem. Beispielsweise stellt das Lesen oder Ändern der gespeicherten Farbe eines Benutzeroberflächenelements für den Benutzer oder die Organisation kein ernsthaftes Sicherheitsrisiko dar. Vermeiden Sie, dass Benutzer sensible Daten überprüfen oder manipulieren können.
ASP.NET Core Geschützter Browser-Speicher
ASP.NET Core Protected Browser Storage nutzt ASP.NET Core-Datenschutz für localStorage und sessionStorage.
Hinweis
Der geschützte Browserspeicher basiert auf ASP.NET Core-Datenschutz und wird nur für serverseitige Blazor-Apps unterstützt.
Warnung
Microsoft.AspNetCore.ProtectedBrowserStorage ist ein nicht unterstütztes, experimentelles Paket, das nicht für den Einsatz in der Produktion gedacht ist.
Das Paket ist nur für die Verwendung in ASP.NET Core 3.1 -Apps verfügbar.
Konfiguration
Fügen Sie
Microsoft.AspNetCore.ProtectedBrowserStorageeinen Paketverweis hinzu.Hinweis
Eine Anleitung zum Hinzufügen von Paketen zu .NET-Anwendungen finden Sie in den Artikeln unter Pakete installieren und verwalten unter Workflow für die Paketnutzung (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.
Fügen Sie in der Datei
_Host.cshtmldas folgende Skript innerhalb des schließenden Tags</body>hinzu:<script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>Rufen Sie in
Startup.ConfigureServicesAddProtectedBrowserStorageauf, um der Dienstsammlung die DienstelocalStorageundsessionStoragehinzuzufügen:services.AddProtectedBrowserStorage();
Speichern und Laden von Daten in einer Komponente
Verwenden Sie für jede Komponente, die das Laden oder Speichern von Daten im Browserspeicher erfordert, die @inject-Anweisung, um eine Instanz von einem der folgenden Komponenten einzufügen:
ProtectedLocalStorageProtectedSessionStorage
Die Auswahl hängt davon ab, welchen Browserspeicherort Sie verwenden möchten. Im folgenden Beispiel wird sessionStorage verwendet:
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
Die @using-Anweisung kann in einer Datei _Imports.razor der App anstatt in der Komponente platziert werden. Bei Verwendung der _Imports.razor-Datei wird der Namespace für größere Segmente der App oder die gesamte App zur Verfügung gestellt.
Um den currentCount-Wert in der Counter-Komponente einer App beizubehalten, die auf der Blazor-Projektvorlage basiert, ändern Sie die IncrementCount-Methode so, dass verwendet wirdProtectedSessionStore.SetAsync:
private async Task IncrementCount()
{
currentCount++;
await ProtectedSessionStore.SetAsync("count", currentCount);
}
Bei größeren, realistischeren Apps ist die Speicherung einzelner Felder ein unwahrscheinliches Szenario. Apps speichern meist ganze Modellobjekte, die komplexe Zustände enthalten.
ProtectedSessionStore serialisiert und deserialisiert JSON-Daten automatisch, um komplexe Zustandsobjekte zu speichern.
Im vorangehenden Codebeispiel werden die currentCount-Daten als sessionStorage['count'] im Browser des Benutzers gespeichert. Die Daten werden nicht als Nur-Text gespeichert, sondern mit der Datenschutzlösung von ASP.NET Core geschützt. Die verschlüsselten Daten können untersucht werden, wenn sessionStorage['count'] in der Entwicklerkonsole des Browsers ausgewertet wird.
Verwenden Sie currentCount, um die Counter-Daten wiederherzustellen, wenn der Benutzer zu einem späteren Zeitpunkt zur ProtectedSessionStore.GetAsync-Komponente zurückkehrt (auch wenn er sich in einer neuen Verbindung befindet):
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");
}
Wenn die Parameter der Komponente den Navigationszustand enthalten, rufen Sie ProtectedSessionStore.GetAsync auf, und weisen Sie ein Ergebnis ungleich null in OnParametersSetAsync zu, nicht OnInitializedAsync.
OnInitializedAsync wird nur ein Mal aufgerufen, wenn die Komponente zum ersten Mal instanziiert wird.
OnInitializedAsync wird später nicht nochmals aufgerufen, wenn der Benutzer zu einer anderen URL navigiert, während er auf der gleichen Seite bleibt. Weitere Informationen finden Sie unter Lebenszyklus von Razor-Komponenten in ASP.NET Core.
Warnung
Die Beispiele in diesem Abschnitt funktionieren nur, wenn für den Server kein Prerendering aktiviert ist. Bei aktiviertem PreRendering wird ein Fehler generiert, der besagt, dass JavaScript-Interop-Aufrufe nicht ausgegeben werden können, weil PreRendering für die Komponente ausgeführt wird.
Deaktivieren Sie entweder Prerendering, oder fügen Sie zusätzlichen Code hinzu, um mit Prerendering zu arbeiten. Weitere Informationen zum Schreiben von Code, der mit Prerendering kompatibel ist, finden Sie im Abschnitt Handhabung von Prerendering.
Handhabung des Ladezustands
Da die Browserspeicherung asynchron über eine Netzwerkverbindung erfolgt, vergeht immer eine gewisse Zeit, bis die Daten geladen werden und für eine Komponente zur Verfügung stehen. Die besten Ergebnisse erzielen Sie, wenn Sie während des Ladevorgangs eine Meldung rendern, anstatt leere oder Standarddaten anzuzeigen.
Ein Ansatz besteht darin, nachzuverfolgen, ob die Daten den Zustand null aufweisen. Dies bedeutet, dass die Daten noch geladen werden. In der Counter-Standardkomponente wird die Anzahl in einer int gespeichert.
Legen Sie fest, dass currentCount NULL-Werte aufweisen kann, indem Sie dem Typ (?) ein Fragezeichen (int) hinzufügen:
private int? currentCount;
Anstatt bedingungslos die Anzahl und die Schaltfläche Increment anzuzeigen, zeigen Sie diese Elemente nur dann an, wenn die Daten geladen wurden, indem Sie HasValue überprüfen:
@if (currentCount.HasValue)
{
<p>Current count: <strong>@currentCount</strong></p>
<button @onclick="IncrementCount">Increment</button>
}
else
{
<p>Loading...</p>
}
Handhabung von Prerendering
Während des Prerenderings:
- Eine interaktive Verbindung zum Browser des Benutzers ist nicht vorhanden.
- Der Browser verfügt noch nicht über eine Seite, auf der der JavaScript-Code ausgeführt werden kann.
localStorage oder sessionStorage sind während des Prerenderings nicht verfügbar. Wenn die Komponente versucht,mit dem Speicher zu interagieren, wird ein Fehler generiert, der besagt, dass JavaScript-Interop-Aufrufe nicht ausgegeben werden können, weil PreRendering für die Komponente ausgeführt wird.
Eine Möglichkeit, den Fehler zu beheben, besteht darin, das Prerendering zu deaktivieren. Dies ist normalerweise die beste Lösung, wenn die App den browserbasierten Speicher stark nutzt. Das Prerendering erhöht die Komplexität und bringt der App keinen Nutzen, da die App keinen nützlichen Inhalt vorab rendern kann, bevor nicht localStorage oder sessionStorage verfügbar sind.
Um das Vorabrendering zu deaktivieren, geben Sie den Renderingmodus an, indem Sie den prerender-Parameter auf false die höchste Komponente in der Komponentenhierarchie der Anwendung setzen, die keine Stammkomponente ist.
Hinweis
Die Interaktivität einer Stammkomponente, wie z. B. der App-Komponente, wird nicht unterstützt. Daher kann das Prerendering nicht direkt von der App-Komponente deaktiviert werden.
Bei Apps, die auf der Blazor Web App-Projektvorlage basieren, ist das Voarabrendering in der Regel deaktiviert, wenn die Routes-Komponente in der App-Komponente (Components/App.razor) verwendet wird:
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
Deaktivieren Sie außerdem das Vorabrendering für die HeadOutlet-Komponente:
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
Weitere Informationen finden Sie unter Prerender ASP.NET Core-KomponentenRazor.
Öffnen Sie die Datei _Host.cshtml, und ändern Sie das render-mode-Attribut des Komponententag-Hilfsprogramms in Server, um PreRendering zu deaktivieren:
<component type="typeof(App)" render-mode="Server" />
Wenn das Prerendering deaktiviert ist, ist das Prerendering von <head>-Inhalten deaktiviert.
Das Prerendering ist möglicherweise nützlich für andere Seiten, die localStorage oder sessionStorage nicht verwenden. Damit PreRendering beibehalten wird, verschieben Sie den Ladevorgang, bis der Browser mit der Verbindung verbunden ist. Im Folgenden finden Sie ein Beispiel für das Speichern eines Zählerwerts:
@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);
}
}
Das Bewahren des Zustands auf einen gemeinsamen Dienstanbieter auslagern.
Wenn viele Komponenten auf die browserbasierte Speicherung zurückgreifen, führt das Implementieren des Zustandsanbietercodes häufig zur Duplizierung des Codes. Eine Möglichkeit, die Duplizierung von Code zu vermeiden, besteht darin, eine übergeordnete Komponente des Zustandsanbieters zu erstellen, die die Zustandsanbieterlogik kapselt. Untergeordnete Komponenten können ohne Berücksichtigung des Zustandspersistenzmechanismus mit beibehaltenen Daten arbeiten.
Im folgenden Beispiel einer CounterStateProvider Komponente werden Zählerdaten in sessionStorage gespeichert. Die Komponente behandelt die Ladephase, indem sie den untergeordneten Inhalt erst rendert, wenn das Laden des Zustands abgeschlossen ist.
Die CounterStateProvider-Komponente befasst sich mit der Vorrendering, indem sie den Zustand erst nach dem Rendern der Komponente in der OnAfterRenderAsync-Lebenszyklusmethode lädt, die während der Vorrendering nicht ausgeführt wird.
Der Ansatz in diesem Abschnitt kann nicht dazu führen, dass mehrere abonnierte Komponenten auf derselben Seite erneut angezeigt werden. Wenn eine abonnierte Komponente den Zustand ändert, wird sie neu gerendert und kann den aktualisierten Zustand anzeigen. Eine andere Komponente auf derselben Seite, die diesen Zustand anzeigt, zeigt jedoch veraltete Daten an, bis sie selbst erneut gerendert wird. Daher eignet sich der in diesem Abschnitt beschriebene Ansatz am besten für die Verwendung des Zustands in einer einzelnen Komponente auf der Seite.
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);
}
}
Hinweis
Weitere Informationen zu RenderFragment finden Sie unter ASP.NET Core Razor Komponenten.
Um den Zustand für alle Komponenten in einer App zugänglich zu machen, umschließen Sie die CounterStateProvider-Komponente um den Router (<Router>...</Router>) in der Routes-Komponente mit globalem interaktivem serverseitigem Rendering (interaktivem SSR).
In der App-Komponente (Components/App.razor):
<Routes @rendermode="InteractiveServer" />
In der Routes-Komponente (Components/Routes.razor):
Um die CounterStateProvider-Komponente zu verwenden, umschließen Sie eine Instanz der Komponente in einer beliebigen anderen Komponente, die Zugriff auf den Zählerzustand benötigt. Um den Zustand für alle Komponenten in einer App zugänglich zu machen, umschließen Sie die CounterStateProvider-Komponente um den Router in der App-Komponente (App.razor):
<CounterStateProvider>
<Router ...>
...
</Router>
</CounterStateProvider>
Hinweis
Mit der Veröffentlichung von .NET 5.0.1 und für alle zusätzlichen 5.x-Versionen enthält die Router Komponente den Parameter PreferExactMatches, festgelegt auf @true. Weitere Informationen finden Sie unter Migrieren von ASP.NET Core 3.1 zu .NET 5.
Umschließende Komponenten erhalten den beibehaltenen Zählerzustand und können diesen ändern. Die folgende Counter-Komponente implementiert das Muster:
@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();
}
}
}
Die vorherige Komponente muss weder mit ProtectedBrowserStorage interagieren, noch eine „Ladephase“ verarbeiten.
In folgenden Fällen wird im Allgemeinen das Muster der übergeordneten Komponente des Zustandsanbieters empfohlen:
- Um den Zustand über viele Komponenten hinweg zu nutzen.
- Es muss nur ein Zustandsobjekt auf der obersten Ebene beibehalten werden.
Um viele verschiedene Zustandsobjekte beizubehalten und verschiedene Teilmengen von Objekten an verschiedenen Orten zu verarbeiten, ist es besser, das globale persistente Speichern des Zustands zu vermeiden.
ASP.NET Core