Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W przypadku danych przejściowych, które użytkownik aktywnie tworzy, często używaną lokalizacją magazynu są kolekcje localStorage i sessionStorage przeglądarki.
-
localStoragejest powiązana z instancją przeglądarki. Jeśli użytkownik ponownie załaduje stronę lub zamknie i ponownie otworzy przeglądarkę, stan będzie się powtarzać. Jeśli użytkownik otworzy wiele kart przeglądarki, stan będzie współdzielony we wszystkich kartach. Dane są utrwalanelocalStoragedo momentu jawnego wyczyszczenia. DanelocalStoragedokumentu załadowane w sesji "przeglądania prywatnego" lub "trybie incognito" są czyszczone po zamknięciu ostatniej karty "prywatnej". -
sessionStoragejest ograniczona do karty przeglądarki. Jeśli użytkownik ponownie załaduje kartę, stan będzie się powtarzać. Jeśli użytkownik zamknie kartę lub przeglądarkę, stan zostanie utracony. Jeśli użytkownik otworzy wiele kart przeglądarki, każda karta ma własną niezależną wersję danych.
Ogólnie rzecz biorąc, sessionStorage jest bezpieczniejszy do użycia.
sessionStorage pozwala uniknąć ryzyka, że użytkownik otwiera wiele kart i napotyka następujące elementy:
- Usterki w przechowywaniu stanu w zakładkach.
- Mylące zachowanie, gdy karta zastępuje stan innych kart.
localStorage jest lepszym wyborem, jeśli aplikacja musi zachować stan podczas zamykania i ponownego otwierania przeglądarki.
Zastrzeżenia dotyczące korzystania z pamięci przeglądarki:
- Podobnie jak w przypadku korzystania z bazy danych po stronie serwera, ładowanie i zapisywanie danych jest asynchroniczne.
- Żądana strona nie istnieje w przeglądarce podczas prerenderingu, więc magazyn lokalny nie jest dostępny podczas prerenderingu.
- Przechowywanie kilku kilobajtów danych jest uzasadnione dla aplikacji po stronie serwera Blazor. Poza kilkoma kilobajtami należy wziąć pod uwagę implikacje dotyczące wydajności, ponieważ dane są ładowane i zapisywane w sieci.
- Użytkownicy mogą wyświetlać lub modyfikować dane. ASP.NET Core Data Protection może ograniczyć ryzyko. Na przykład ASP.NET Core Protected Browser Storage używa ASP.NET Core Data Protection.
Pakiety NuGet innych firm udostępniają interfejsy API do pracy z localStorage i sessionStorage. Warto rozważyć wybranie pakietu, który w sposób niewidoczny używa ASP.NET Core Data Protection. Usługa Data Protection szyfruje przechowywane dane i zmniejsza potencjalne ryzyko naruszenia przechowywanych danych. Jeśli dane serializowane w formacie JSON są przechowywane w postaci zwykłego tekstu, użytkownicy mogą wyświetlać dane przy użyciu narzędzi deweloperskich przeglądarki, a także modyfikować przechowywane dane. Zabezpieczanie danych trywialnych nie jest problemem. Na przykład odczytywanie lub modyfikowanie przechowywanego koloru elementu interfejsu użytkownika nie jest istotnym zagrożeniem bezpieczeństwa dla użytkownika lub organizacji. Unikaj zezwalania użytkownikom na inspekcję lub manipulowanie poufnymi danymi.
Ochrona pamięci przeglądarki w ASP.NET Core
ASP.NET Core Protected Browser Storage korzysta z usługi ASP.NET Core Data Protection dla localStorage i sessionStorage.
Uwaga / Notatka
Usługa Protected Browser Storage korzysta z ASP.NET Core Data Protection i jest obsługiwana tylko w przypadku aplikacji po stronie Blazor serwera.
Ostrzeżenie
Microsoft.AspNetCore.ProtectedBrowserStorage to nieobsługiwany, eksperymentalny pakiet, który nie jest przeznaczony do użytku produkcyjnego.
Pakiet jest dostępny tylko do użycia w aplikacjach platformy ASP.NET Core 3.1.
Konfiguracja
Dodaj odwołanie do pakietu
Microsoft.AspNetCore.ProtectedBrowserStorage.Uwaga / Notatka
Aby uzyskać wskazówki dotyczące dodawania pakietów do aplikacji .NET, zobacz artykuły w Instalowanie i zarządzanie pakietami oraz w proces korzystania z pakietów (dokumentacja NuGet). Sprawdź prawidłowe wersje pakietów pod adresem NuGet.org.
_Host.cshtmlW pliku dodaj następujący skrypt wewnątrz tagu zamykającego</body>:<script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>W
Startup.ConfigureServicespliku wywołajAddProtectedBrowserStorage, aby dodaćlocalStorageisessionStoragedo kolekcji usług:services.AddProtectedBrowserStorage();
Zapisywanie i ładowanie danych w składniku
W każdym składniku, który wymaga ładowania lub zapisywania danych w magazynie przeglądarki, użyj dyrektywy @inject, aby wstrzyknąć wystąpienie jednego z następujących:
ProtectedLocalStorageProtectedSessionStorage
Wybór zależy od lokalizacji przechowywania przeglądarki, której chcesz użyć. W poniższym przykładzie sessionStorage jest używany:
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
Dyrektywę @using można umieścić w pliku aplikacji _Imports.razor zamiast w składniku.
_Imports.razor Użycie pliku sprawia, że przestrzeń nazw jest dostępna dla większych segmentów aplikacji lub całej aplikacji.
Aby zachować wartość currentCount w składniku Counter aplikacji na podstawie szablonu projektu Blazor, zmodyfikuj metodę IncrementCount, aby użyć ProtectedSessionStore.SetAsync.
private async Task IncrementCount()
{
currentCount++;
await ProtectedSessionStore.SetAsync("count", currentCount);
}
W większych, bardziej realistycznych aplikacjach przechowywanie poszczególnych pól jest mało prawdopodobne. Aplikacje częściej przechowują całe obiekty modelu, które obejmują złożony stan.
ProtectedSessionStore automatycznie serializuje i deserializuje dane JSON w celu przechowywania złożonych obiektów stanu.
W poprzednim przykładzie kodu, dane currentCount są przechowywane jako sessionStorage['count'] w przeglądarce użytkownika. Dane nie są przechowywane w postaci zwykłego tekstu, ale są chronione przy użyciu ASP.NET Core Data Protection. Zaszyfrowane dane można sprawdzić, jeśli sessionStorage['count'] jest oceniany w konsoli programisty przeglądarki.
Aby odzyskać dane currentCount, gdy użytkownik powróci do składnika Counter, nawet jeśli znajduje się w nowym okręgu, użyj 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");
}
Jeśli parametry składnika obejmują stan nawigacji, wywołaj ProtectedSessionStore.GetAsync i przypisz wynik w null inny niż OnParametersSetAsync, a nie w OnInitializedAsync.
OnInitializedAsync jest wywoływany tylko raz, gdy składnik jest najpierw instancjonowany.
OnInitializedAsync nie zostanie ponownie wywołana później, jeśli użytkownik przejdzie do innego adresu URL, pozostając na tej samej stronie. Aby uzyskać więcej informacji, zobacz Cykl życia składników platformy ASP.NET Core Razor.
Ostrzeżenie
Przykłady w tej sekcji działają tylko wtedy, gdy serwer nie ma włączonego prerenderingu. Po włączeniu prerenderingu jest generowany błąd wyjaśniający, że nie można wykonać wywołań międzyoperacyjnych JavaScript, ponieważ składnik jest wstępnie renderowany.
Wyłącz prerendering lub dodaj dodatkowy kod, aby pracować z prerenderingiem. Aby dowiedzieć się więcej na temat pisania kodu, który działa z prerenderingiem, zobacz sekcję Obsługa prerenderingu .
Obsługa stanu ładowania
Ponieważ dostęp do magazynu przeglądarki jest uzyskiwany asynchronicznie za pośrednictwem połączenia sieciowego, zawsze istnieje okres czasu przed załadowaniem i udostępnieniem danych składnikowi. Aby uzyskać najlepsze wyniki, zamiast wyświetlania pustych lub domyślnych danych, renderuj komunikat podczas trwania ładowania.
Jednym z podejść jest śledzenie, czy dane są null, co oznacza, że dane są nadal ładowane. W składniku domyślnym Counter liczba jest przechowywana w elemencie int.
Uczyń currentCount typu nullable, dodając znak zapytania (?) do typu (int):
private int? currentCount;
Zamiast bezwarunkowo wyświetlać liczbę i Increment przycisk, wyświetl te elementy tylko wtedy, gdy dane są ładowane, sprawdzając polecenie HasValue:
@if (currentCount.HasValue)
{
<p>Current count: <strong>@currentCount</strong></p>
<button @onclick="IncrementCount">Increment</button>
}
else
{
<p>Loading...</p>
}
Obsługa prerenderingu
Podczas prerenderingu:
- Interakcyjne połączenie z przeglądarką użytkownika nie istnieje.
- Przeglądarka nie ma jeszcze strony, na której można uruchomić kod JavaScript.
localStorage lub sessionStorage nie są dostępne podczas prerenderingu. Jeśli składnik próbuje wchodzić w interakcję z pamięcią, zostanie wygenerowany błąd wyjaśniający, że nie można wydać wywołań interop w JavaScript, ponieważ składnik jest prerenderowany.
Jednym ze sposobów usunięcia błędu jest wyłączenie prerenderingu. Zazwyczaj jest to najlepszy wybór, jeśli aplikacja intensywnie korzysta z magazynu przechowywania danych opartego na przeglądarce. Wstępne renderowanie zwiększa złożoność i nie przynosi korzyści aplikacji, ponieważ nie może ona wstępnie wyrenderować żadnej użytecznej zawartości, dopóki localStorage lub sessionStorage nie będą dostępne.
Aby wyłączyć prerendering, wskaż tryb renderowania z parametrem prerender ustawionym na false w składniku na najwyższym poziomie hierarchii składników aplikacji, który nie jest składnikiem głównym.
Uwaga / Notatka
Tworzenie interakcyjnego składnika głównego, takiego jak składnik App, nie jest wspierane. W związku z tym prerendering nie może być bezpośrednio wyłączony przez komponent App.
W przypadku aplikacji opartych na szablonie projektu Blazor Web App, prerendering jest zwykle wyłączony, gdy składnik Routes jest używany w składniku App (Components/App.razor).
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
Ponadto wyłącz prerendering dla HeadOutlet komponentu:
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
Aby uzyskać więcej informacji, zobacz Razor.
Aby wyłączyć prerendering, otwórz plik _Host.cshtml i zmień atrybut render-mode pomocnika tagów składnika na Server:
<component type="typeof(App)" render-mode="Server" />
Gdy prerenderowanie jest wyłączone, prerenderowanie zawartości
Wstępne przetwarzanie może być przydatne w przypadku innych stron, które nie używają localStorage ani sessionStorage. Aby zachować wstępne ładowanie, odrocz operację ładowania, dopóki przeglądarka nie zostanie połączona z obwodem. Poniżej przedstawiono przykład przechowywania wartości licznika:
@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);
}
}
Uwzględnianie zachowania stanu dla wspólnego dostawcy
Jeśli wiele składników korzysta z magazynu opartego na przeglądarce, implementowanie kodu dostawcy stanu wiele razy powoduje duplikowanie kodu. Jedną z opcji unikania duplikowania kodu jest utworzenie składnika nadrzędnego dostawcy stanu, który hermetyzuje logikę dostawcy stanu. Składniki podrzędne mogą pracować z utrwalanymi danymi bez względu na mechanizm trwałości stanu.
W poniższym przykładzie składnika CounterStateProvider dane licznika są zapisywane do sessionStorage, a faza ładowania jest obsługiwana poprzez nierealizowanie renderowania zawartości podrzędnej do zakończenia ładowania stanu.
Składnik CounterStateProvider zajmuje się prerenderowaniem, nie ładując stanu aż do momentu po renderowaniu składnika w metodzie cyklu życia OnAfterRenderAsync, która nie jest wykonywana podczas prerenderowania.
Podejście opisane w tej sekcji nie jest w stanie wyzwolić ponownego renderowania wielu związanych komponentów na tej samej stronie. Jeśli jeden z subskrybowanych składników zmienia stan, ponownie się renderuje i może wyświetlać zaktualizowany stan, natomiast inny składnik na tej samej stronie, wyświetlający ten stan, pokazuje nieaktualne dane do czasu swojego kolejnego ponownego renderowania. W związku z tym podejście opisane w tej sekcji najlepiej nadaje się do używania stanu w jednym składniku na stronie.
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);
}
}
Uwaga / Notatka
Aby uzyskać więcej informacji na temat RenderFragment, zobacz ASP.NET Core Razor components.
Aby stan był dostępny dla wszystkich składników w aplikacji, owijaj składnik CounterStateProvider wokół składnika Router (<Router>...</Router>) w składniku Routes z globalnym interaktywnym renderowaniem po stronie serwera (interaktywne SSR).
W składniku App (Components/App.razor):
<Routes @rendermode="InteractiveServer" />
W składniku Routes (Components/Routes.razor):
Aby użyć CounterStateProvider składnika, opakuj wystąpienie składnika wokół dowolnego innego składnika, który wymaga dostępu do stanu licznika. Aby udostępnić stan wszystkim składnikom w aplikacji, owiń składnik CounterStateProvider wokół składnika Router w składniku App (App.razor):
<CounterStateProvider>
<Router ...>
...
</Router>
</CounterStateProvider>
Uwaga / Notatka
Wraz z wydaniem platformy .NET 5.0.1 i wszystkimi dodatkowymi wersjami 5.x składnik zawiera parametr Router ustawiony na wartość PreferExactMatches. Aby uzyskać więcej informacji, zobacz Migrowanie z platformy ASP.NET Core 3.1 do platformy .NET 5.
Zawinięte składniki odbierają i mają możliwość modyfikacji stanu utrwalonego licznika. Następujący Counter składnik implementuje wzorzec:
@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();
}
}
}
Poprzedni składnik nie jest wymagany do interakcji z ProtectedBrowserStorage, ani nie uczestniczy w fazie "ładowania".
Ogólnie rzecz biorąc, zalecany jest wzorzec składnika nadrzędnego dostawcy stanu:
- Aby korzystać ze stanu w wielu komponentach.
- Jeśli istnieje tylko jeden obiekt stanu najwyższego poziomu do przechowywania.
Aby zachować wiele różnych obiektów stanu i wykorzystywać różne podzbiory obiektów w różnych miejscach, lepiej unikać globalnego zachowywania stanu.
ASP.NET Core