Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Pour les données transitoires que l’utilisateur crée activement, un emplacement de stockage couramment utilisé est les collections localStorage et sessionStorage du navigateur :
-
localStorageest limité à l’instance du navigateur. Si l’utilisateur recharge la page ou ferme et rouvre le navigateur, l’état persiste. Si l’utilisateur ouvre plusieurs onglets de navigateur, l’état est partagé entre les onglets. Les données persistent danslocalStoragejusqu’à ce qu’elles soient explicitement effacées. Les donnéeslocalStoraged’un document chargé dans une session de « navigation privée » ou « incognito » sont effacées lorsque le dernier onglet « privé » est fermé. -
sessionStorageest limité à l’onglet du navigateur. Si l’utilisateur recharge l’onglet, l’état persiste. Si l’utilisateur ferme l’onglet ou le navigateur, l’état est perdu. Si l’utilisateur ouvre plusieurs onglets de navigateur, chaque onglet dispose de sa propre version indépendante des données.
En général, sessionStorage est plus sûr à utiliser.
sessionStorage évite le risque qu’un utilisateur ouvre plusieurs onglets et rencontre le problème suivant :
- Bogues dans le stockage d’état entre les onglets.
- Comportement confus lorsqu’un onglet remplace l’état d’autres onglets.
localStorage est le meilleur choix si l’application doit conserver l’état après la fermeture et la réouverture du navigateur.
Précautions relatives à l’utilisation du stockage du navigateur :
- Comme pour l’utilisation d’une base de données côté serveur, le chargement et l’enregistrement des données sont asynchrones.
- La page demandée n’existe pas dans le navigateur pendant le prérendu, donc le stockage local n’est pas disponible pendant le prérendu.
- Le stockage de quelques kilo-octets de données est raisonnable pour les applications Blazor côté serveur. Au-delà de quelques kilo-octets, vous devez tenir compte des implications en termes de performances, car les données sont chargées et enregistrées sur le réseau.
- Les utilisateurs peuvent afficher ou altérer les données. ASP.NET Core Data Protection peut atténuer ce risque. Par exemple, ASP.NET Core Protected Browser Storage utilise ASP.NET Core Data Protection.
Des packages NuGet tiers fournissent des API pour travailler avec localStorage et sessionStorage. Il est intéressant d’envisager de choisir un package qui utilise de manière transparente ASP.NET Core Data Protection. Data Protection chiffre les données stockées et réduit le risque potentiel de falsification des données stockées. Si les données sérialisées au format JSON sont stockées en texte brut, les utilisateurs peuvent les consulter à l’aide des outils de développement du navigateur et également modifier les données stockées. La sécurisation des données triviales ne pose pas de problème. Par exemple, la lecture ou la modification de la couleur stockée d’un élément d’interface utilisateur ne représente pas un risque de sécurité significatif pour l’utilisateur ou l’organisation. Évitez de permettre aux utilisateurs d’inspecter ou de modifier des données sensibles.
Stockage protégé du navigateur ASP.NET Core
ASP.NET Core Protected Browser Storage exploite ASP.NET Core Data Protection pour localStorage et sessionStorage.
Remarque
Protected Browser Storage s’appuie sur ASP.NET Core Data Protection et n’est pris en charge que pour les applications Blazor côté serveur.
Avertissement
Microsoft.AspNetCore.ProtectedBrowserStorageest un package expérimental non pris en charge qui n’est pas destiné à une utilisation en production.
Le package est uniquement disponible pour une utilisation dans les applications ASP.NET Core 3.1.
Paramétrage
Ajoutez une référence de package à
Microsoft.AspNetCore.ProtectedBrowserStorage.Remarque
Pour obtenir des conseils sur l'ajout de packages à des applications .NET, consultez les articles figurant sous Installer et gérer des packages dans Flux de travail de la consommation des packages (documentation NuGet). Vérifiez les versions du package sur NuGet.org.
Dans le fichier
_Host.cshtml, ajoutez le script suivant à l’intérieur de la balise</body>de fermeture :<script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>Dans
Startup.ConfigureServices, appelezAddProtectedBrowserStoragepour ajouter les serviceslocalStorageetsessionStorageà la collection de services :services.AddProtectedBrowserStorage();
Enregistrer et charger des données dans un composant
Dans tout composant qui nécessite le chargement ou l’enregistrement de données dans le stockage du navigateur, utilisez la directive @inject pour injecter une instance de l’un des éléments suivants :
ProtectedLocalStorageProtectedSessionStorage
Le choix dépend de l’emplacement de stockage du navigateur que vous souhaitez utiliser. Dans l’exemple suivant, sessionStorage est utilisé :
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
La directive @using peut être placée dans le fichier _Imports.razor de l’application plutôt que dans le composant. L’utilisation du fichier _Imports.razor rend l’espace de noms disponible pour des segments plus importants de l’application ou pour l’ensemble de l’application.
Pour conserver la valeur currentCount dans le composant Counter d’une application basée sur le modèle de projet Blazor, modifiez la méthode IncrementCount afin d’utiliser ProtectedSessionStore.SetAsync :
private async Task IncrementCount()
{
currentCount++;
await ProtectedSessionStore.SetAsync("count", currentCount);
}
Dans les applications plus grandes et plus réalistes, le stockage de champs individuels est peu probable. Les applications sont plus susceptibles de stocker des objets modèles entiers qui incluent des états complexes.
ProtectedSessionStoreserialise et désérialise automatiquement les données JSON pour stocker des objets d’état complexes.
Dans l’exemple de code précédent, les données currentCount sont stockées sous le nom sessionStorage['count'] dans le navigateur de l’utilisateur. Les données ne sont pas stockées en texte brut, mais sont protégées à l’aide de la protection des données ASP.NET Core. Les données chiffrées peuvent être inspectées si sessionStorage['count'] est évalué dans la console de développement du navigateur.
Pour récupérer les données currentCount si l’utilisateur revient ultérieurement au composant Counter, y compris s’il se trouve sur un nouveau circuit, utilisez 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");
}
Si les paramètres du composant incluent l’état de navigation, appelez ProtectedSessionStore.GetAsync et attribuez un résultat non null à OnParametersSetAsync, et non OnInitializedAsync.
OnInitializedAsyncn’est appelé qu’une seule fois lors de la première instanciation du composant.
OnInitializedAsyncn’est pas appelé à nouveau ultérieurement si l’utilisateur accède à une URL différente tout en restant sur la même page Pour plus d’informations, consultez le cycle de vie des composants Razor ASP.NET Core.
Avertissement
Les exemples de cette section ne fonctionnent que si le serveur n’a pas activé le prérendu Lorsque le prérendu est activé, une erreur est générée pour expliquer que les appels d’interopérabilité JavaScript ne peuvent pas être émis car le composant est en cours de prérendu.
Désactivez le pré-rendu ou ajoutez du code supplémentaire pour travailler correctement avec le pré-rendu. Pour en savoir plus sur l’écriture de code compatible avec le prérendu, consultez la section Gérer le prérendu.
Gérer l’état de chargement
Étant donné que le stockage du navigateur est accessible de manière asynchrone via une connexion réseau, il y a toujours un délai avant que les données ne soient chargées et disponibles pour un composant. Pour obtenir les meilleurs résultats, affichez un message pendant le chargement au lieu d’afficher des données vides ou par défaut.
Une approche consiste à vérifier si les données sont null, ce qui signifie qu’elles sont encore en cours de chargement. Dans le composant Counter par défaut, le nombre est conservé dans un int.
Rendez currentCount nullable en ajoutant un point d’interrogation (?) au type (int) :
private int? currentCount;
Au lieu d’afficher inconditionnellement le nombre et le bouton Increment, affichez ces éléments uniquement si les données sont chargées en vérifiant HasValue :
@if (currentCount.HasValue)
{
<p>Current count: <strong>@currentCount</strong></p>
<button @onclick="IncrementCount">Increment</button>
}
else
{
<p>Loading...</p>
}
Gérer le pré-rendu
Pendant le pré-rendu :
- Il n’existe aucune connexion interactive avec le navigateur de l’utilisateur.
- Le navigateur ne dispose pas encore d’une page dans laquelle il peut exécuter du code JavaScript.
localStorage ou sessionStorage ne sont pas disponibles pendant le pré-rendu. Si le composant tente d’interagir avec le stockage, une erreur est générée pour expliquer que les appels d’interopérabilité JavaScript ne peuvent pas être émis car le composant est en cours de prérendu.
On peut résoudre l’erreur en désactivant le pré-rendu. C’est généralement le meilleur choix si l’application utilise beaucoup le stockage basé sur le navigateur. Le pré-rendu ajoute de la complexité et n’est pas avantageux pour l’application, car elle ne peut pas effectuer de pré-rendu de contenu utile tant que localStorage ou sessionStorage ne sont pas disponibles.
Pour désactiver le prérendu, indiquez le mode de rendu avec le paramètre prerender défini sur false au niveau du composant le plus élevé de la hiérarchie des composants de l’application qui n’est pas un composant racine.
Remarque
La création d’un composant racine interactif, tel que le composant App, n’est pas prise en charge. Par conséquent, le pré-rendu ne peut pas être désactivé directement par le composant App.
Pour les applications basées sur le modèle de projet Blazor Web App, le pré-rendu est habituellement désactivé lorsque le composant Routes est utilisé dans le composant App (Components/App.razor) :
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
Désactivez également le pré-rendu pour le composant HeadOutlet :
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
Pour plus d’informations, consultez Prévisualiser les composants ASP.NET Core Razor.
Pour désactiver le pré-rendu : ouvrez le fichier _Host.cshtml et définissez l’attribut render-mode du Component Tag Helper sur Server :
<component type="typeof(App)" render-mode="Server" />
Lorsque le prérendu est désactivé, le prérendu<head> du contenu est désactivé.
Le pré-rendu peut être utile pour d’autres pages qui n’utilisent pas localStorage ou sessionStorage. Pour conserver le pré-rendu, retardez le processus de chargement jusqu’à ce que le navigateur soit connecté au circuit. Voici un exemple de stockage d’une valeur de compteur :
@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);
}
}
Factoriser la préservation de l'État auprès d'un fournisseur commun
Si de nombreux composants dépendent du stockage basé sur le navigateur, l’implémentation du code du fournisseur d’état à plusieurs reprises crée une duplication du code. Une option pour éviter la duplication de code consiste à créer un composant parent fournisseur d’état qui encapsule la logique du fournisseur d’état. Les composants enfants peuvent fonctionner avec des données persistantes sans tenir compte du mécanisme de persistance d’état.
Dans l'exemple suivant d'un composant CounterStateProvider, les données du compteur sont persistées dans sessionStorage, et il gère la phase de chargement en ne rendant pas le contenu de ses enfants tant que le chargement de l'état n'est pas terminé.
Le composant CounterStateProvider traite de la préversion en ne chargeant pas l’état tant qu’après le rendu du composant dans la méthode de cycle de vie OnAfterRenderAsync, qui ne s’exécute pas pendant la préversion.
L'approche décrite dans cette section ne permet pas de déclencher le rendu de plusieurs composants abonnés sur la même page. Si un composant abonné change l’état, il se re-renderise et peut afficher l’état mis à jour, mais un autre composant sur la même page affichant cet état continue d'afficher des données obsolètes jusqu’à son propre prochain re-rendering. Par conséquent, l’approche décrite dans cette section est la mieux adaptée à l’utilisation de l’état dans un seul composant de la page.
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);
}
}
Remarque
Pour plus d’informations sur RenderFragment, consultez Composants ASP.NET Core Razor.
Pour rendre l’état accessible à tous les composants d’une application, encapsulez le composant CounterStateProvider autour du Router (<Router>...</Router>) dans le composant Routes avec le rendu interactif global côté serveur (SSR interactif).
Dans le composant App (Components/App.razor) :
<Routes @rendermode="InteractiveServer" />
Dans le composant Routes (Components/Routes.razor) :
Pour utiliser le composant CounterStateProvider, encapsulez une instance du composant autour de tout autre composant qui nécessite l’accès à l’état du compteur. Pour rendre l’état accessible à tous les composants d’une application, encapsulez le composant CounterStateProvider autour du composant Router dans le composant App (App.razor) :
<CounterStateProvider>
<Router ...>
...
</Router>
</CounterStateProvider>
Remarque
Avec la version .NET 5.0.1 et pour toute version 5.x ultérieure, le composant Router inclut le paramètre PreferExactMatches défini sur @true. Pour plus d’informations, consultez Migrer de ASP.NET Core 3.1 vers .NET 5.
Les composants encapsulés reçoivent et peuvent modifier l’état persistant du compteur. Le composant Counter suivant implémente le modèle :
@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();
}
}
}
Le composant précédent n’est pas nécessaire pour interagir avec ProtectedBrowserStorage, ni pour gérer une phase de « chargement ».
En règle générale, le modèle de composant parent du fournisseur d’état est recommandé :
- Pour consommer l’état sur de nombreux composants.
- S’il n’y a qu’un seul objet d’état de niveau supérieur à persister.
Pour persister de nombreux objets d’état différents et consommer différents sous-ensembles d’objets à différents endroits, il est préférable d’éviter de persister l’état globalement.