Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Para dados transitórios que o utilizador está a criar ativamente, um local de armazenamento comumente usado são as coleções localStorage e sessionStorage no navegador.
-
localStorageestá limitado ao escopo da instância do navegador. Se o usuário recarregar a página ou fechar e reabrir o navegador, o estado persistirá. Se o utilizador abrir várias guias do navegador, o estado será compartilhado entre as guias. Os dados persistem emlocalStorageaté serem explicitamente apagados. Os dadoslocalStoragede um documento carregado numa sessão de "navegação privada" ou "anónima" são limpos quando o último separador "privado" é fechado. -
sessionStoragetem como escopo a guia do navegador. Se o usuário recarregar a guia, o estado persistirá. Se o usuário fechar a guia ou o navegador, o estado será perdido. Se o usuário abrir várias guias do navegador, cada guia terá sua própria versão independente dos dados.
Geralmente, sessionStorage é mais seguro de usar.
sessionStorage evita o risco de um utilizador abrir várias abas e encontrar o seguinte:
- Bugs no armazenamento de estado entre guias.
- Comportamento confuso quando uma aba substitui o estado de outras abas.
localStorage é a melhor opção se a aplicação precisar manter o estado ao fechar e reabrir o navegador.
Advertências para o uso do armazenamento do navegador:
- Semelhante ao uso de um banco de dados do lado do servidor, o carregamento e o salvamento de dados são assíncronos.
- A página solicitada não existe no navegador durante a pré-renderização, portanto, o armazenamento local não está disponível durante a pré-renderização.
- O armazenamento de alguns kilobytes de dados é razoável para persistir para aplicativos Blazor do lado do servidor. Além de alguns kilobytes, você deve considerar as implicações de desempenho porque os dados são carregados e salvos em toda a rede.
- Os usuários podem visualizar ou adulterar os dados. A proteção de dados do ASP.NET Core pode mitigar o risco. Por exemplo, ASP.NET Core Protected Browser Storage usa ASP.NET Core Data Protection.
Os pacotes NuGet de terceiros fornecem APIs para trabalhar com localStorage e sessionStorage. Vale a pena considerar a escolha de um pacote que use de forma transparente ASP.NET Core Data Protection. A Proteção de Dados encripta os dados armazenados e reduz o risco potencial de adulteração dos dados armazenados. Se os dados serializados por JSON forem armazenados em texto sem formatação, os usuários poderão ver os dados usando as ferramentas de desenvolvedor do navegador e também modificar os dados armazenados. Proteger dados triviais não é um problema. Por exemplo, ler ou modificar a cor armazenada de um elemento da interface do usuário não é um risco de segurança significativo para o usuário ou a organização. Evite permitir que os usuários inspecionem ou adulterem dados confidenciais.
Armazenamento Protegido do Navegador ASP.NET Core
ASP.NET Core Protected Browser Storage aproveita ASP.NET Core Data Protection para localStorage e sessionStorage.
Observação
O Armazenamento Protegido do Navegador depende do ASP.NET Core Data Protection e só é suportado para aplicações Blazor do lado do servidor.
Advertência
Microsoft.AspNetCore.ProtectedBrowserStorage é um pacote experimental sem suporte que não se destina ao uso em produção.
O pacote só está disponível para uso em aplicativos ASP.NET Core 3.1.
Configuração
Adicione uma referência de pacote a
Microsoft.AspNetCore.ProtectedBrowserStorage.Observação
Para obter orientação sobre como adicionar pacotes a aplicativos .NET, consulte os artigos na seção Instalar e gerenciar pacotes em Workflow de utilização de pacotes (documentação do NuGet). Confirme as versões corretas do pacote em NuGet.org.
No arquivo
_Host.cshtml, adicione o seguinte script dentro da etiqueta</body>de fechamento.<script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>No
Startup.ConfigureServices, chameAddProtectedBrowserStoragepara adicionar os serviçoslocalStorageesessionStorageà coleção de serviços:services.AddProtectedBrowserStorage();
Salvar e carregar dados em um componente
Em qualquer componente que exija o carregamento ou o salvamento de dados no armazenamento do navegador, use a diretiva @inject para injetar uma instância de um dos seguintes:
ProtectedLocalStorageProtectedSessionStorage
A escolha depende do local de armazenamento do navegador que você deseja usar. No exemplo a seguir, sessionStorage é usado:
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
A diretiva @using pode ser colocada no arquivo de _Imports.razor do aplicativo em vez de no componente. O uso do arquivo _Imports.razor torna o namespace disponível para segmentos maiores do aplicativo ou do aplicativo inteiro.
Para persistir o valor currentCount no componente Counter de um aplicativo com base no modelo de projeto Blazor, modifique o método IncrementCount para usar ProtectedSessionStore.SetAsync:
private async Task IncrementCount()
{
currentCount++;
await ProtectedSessionStore.SetAsync("count", currentCount);
}
Em aplicativos maiores e mais realistas, o armazenamento de campos individuais é um cenário improvável. Os aplicativos são mais propensos a armazenar objetos de modelo inteiros que incluem estado complexo.
ProtectedSessionStore serializa e desserializa automaticamente dados JSON para armazenar objetos de estado complexos.
No exemplo de código anterior, os dados currentCount são armazenados como sessionStorage['count'] no navegador do usuário. Os dados não são armazenados em texto simples, mas são protegidos usando ASP.NET Proteção de Dados Principais. Os dados criptografados podem ser inspecionados se sessionStorage['count'] for avaliado no console do desenvolvedor do navegador.
Para recuperar os dados currentCount se o utilizador regressar ao componente Counter mais tarde, inclusive se o utilizador estiver num novo circuito, use 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");
}
Se os parâmetros do componente incluírem o estado de navegação, chame ProtectedSessionStore.GetAsync e atribua um resultado nãonull em OnParametersSetAsync, não OnInitializedAsync.
OnInitializedAsync só é chamado uma vez quando o componente é instanciado pela primeira vez.
OnInitializedAsync não será chamado novamente mais tarde se o usuário navegar para um URL diferente enquanto permanece na mesma página. Para obter mais informações, consulte o ciclo de vida do componente ASP.NET Core Razor.
Advertência
Os exemplos nesta seção só funcionam se o servidor não tiver a pré-renderização habilitada. Com a pré-renderização ativada, um erro é gerado explicando que as chamadas de interoperabilidade JavaScript não podem ser emitidas porque o componente está sendo pré-renderizado.
Desative a pré-renderização ou adicione código adicional para trabalhar com a pré-renderização. Para saber mais sobre como escrever código que funciona com a pré-renderização, consulte a seção Manipular pré-renderização.
Manipular o estado de carregamento
Como o armazenamento do navegador é acessado de forma assíncrona por meio de uma conexão de rede, sempre há um período de tempo antes que os dados sejam carregados e estejam disponíveis para um componente. Para obter os melhores resultados, renderize uma mensagem enquanto o carregamento está em andamento em vez de exibir dados em branco ou padrão.
Uma abordagem é controlar se os dados estão null, o que significa que os dados ainda estão sendo carregados. No componente Counter padrão, a contagem é mantida em um int.
Torne currentCount anulável adicionando um ponto de interrogação (?) ao tipo (int):
private int? currentCount;
Em vez de exibir incondicionalmente o botão de contagem e Increment, exiba esses elementos somente se os dados forem carregados verificando HasValue:
@if (currentCount.HasValue)
{
<p>Current count: <strong>@currentCount</strong></p>
<button @onclick="IncrementCount">Increment</button>
}
else
{
<p>Loading...</p>
}
Manipular a pré-renderização
Durante a pré-renderização:
- Não existe uma ligação interativa ao navegador do utilizador.
- O navegador ainda não tem uma página na qual possa executar código JavaScript.
localStorage ou sessionStorage não estão disponíveis durante a pré-renderização. Se o componente tentar interagir com o armazenamento, um erro será gerado explicando que as chamadas de interoperabilidade JavaScript não podem ser emitidas porque o componente está sendo pré-renderizado.
Uma maneira de resolver o erro é desativar a pré-renderização. Essa geralmente é a melhor escolha se o aplicativo fizer uso intenso do armazenamento baseado em navegador. A pré-renderização adiciona complexidade e não beneficia o aplicativo porque o aplicativo não pode pré-renderizar nenhum conteúdo útil até que localStorage ou sessionStorage estejam disponíveis.
Para desativar a pré-renderização, indique o modo de renderização com o parâmetro prerender definido como false no componente de nível mais alto na hierarquia de componentes do aplicativo que não seja um componente raiz.
Observação
Não há suporte para tornar um componente raiz interativo, como o componente App. Portanto, a pré-renderização não pode ser desabilitada diretamente pelo componente App.
Para aplicativos baseados no modelo de projeto Blazor Web App, a pré-renderização normalmente é desabilitada quando o componente Routes é usado no componente App (Components/App.razor):
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
Além disso, desative a pré-renderização para o componente HeadOutlet:
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
Para obter mais informações, consulte componentes do ASP.NET Core pré-renderizadosRazor.
Para desativar a pré-renderização, abra o arquivo _Host.cshtml e altere o atributo render-mode do Tag Helper de Componentes para Server:
<component type="typeof(App)" render-mode="Server" />
Quando a pré-renderização está desativada, <head> está desativado.
A pré-renderização pode ser útil para outras páginas que não usam localStorage ou sessionStorage. Para manter a pré-renderização, adie a operação de carregamento até que o navegador esteja conectado ao circuito. Segue-se um exemplo para armazenar um valor de contador:
@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);
}
}
Excluir a preservação do estado para um provedor comum
Se muitos componentes dependem de armazenamento baseado em navegador, a implementação do código do provedor de estado muitas vezes cria duplicação de código. Uma opção para evitar a duplicação de código é criar um componente pai do provedor de estado que encapsula a lógica do provedor de estado. Os componentes filho podem trabalhar com dados persistentes sem se preocupar com o mecanismo de persistência de estado.
No exemplo a seguir de um componente CounterStateProvider, os dados do contador são persistidos em sessionStorage, e ele lida com a fase de carregamento ao não renderizar o conteúdo filho até que o carregamento de estado esteja concluído.
O componente CounterStateProvider gere a pré-renderização ao não carregar o estado até após a renderização do componente no método de ciclo de vida OnAfterRenderAsync, que não é executado durante a pré-renderização.
A abordagem nesta secção não é capaz de acionar a rerenderização de vários componentes inscritos na mesma página. Se um componente inscrito alterar o estado, ele será renderizado novamente e poderá exibir o estado atualizado. No entanto, outro componente na mesma página exibindo esse estado mostrará dados obsoletos até a sua própria nova renderização. Portanto, a abordagem descrita nesta seção é mais adequada para usar o estado em um único componente na página.
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);
}
}
Observação
Para obter mais informações sobre RenderFragment, consulte componentes do ASP.NET Core Razor.
Para tornar o estado acessível a todos os componentes em um aplicativo, envolva o componente CounterStateProvider em torno do Router (<Router>...</Router>) no componente Routes com renderização interativa global do lado do servidor (SSR interativo).
No componente App (Components/App.razor):
<Routes @rendermode="InteractiveServer" />
No componente Routes (Components/Routes.razor):
Para usar o componente CounterStateProvider, envolva uma instância do componente em torno de qualquer outro componente que exija acesso ao estado do contador. Para tornar o estado acessível a todos os componentes de um aplicativo, envolva o componente CounterStateProvider ao redor do Router no componente App (App.razor):
<CounterStateProvider>
<Router ...>
...
</Router>
</CounterStateProvider>
Observação
Com o lançamento do .NET 5.0.1 e para quaisquer versões adicionais do 5.x, o Router componente inclui o PreferExactMatches parâmetro definido como @true. Para obter mais informações, consulte Migrar do ASP.NET Core 3.1 para o .NET 5.
Os componentes encapsulados recebem e podem modificar o estado do contador persistente. O seguinte componente Counter implementa o padrão:
@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();
}
}
}
O componente anterior não é necessário para interagir com ProtectedBrowserStorage, nem lida com uma fase de "carregamento".
Em geral, recomenda-se o padrão do componente pai do provedor de estado :
- Para consumir o estado em muitos componentes.
- Se houver apenas um objeto de estado de nível superior para persistir.
Para persistir muitos objetos de estado diferentes e consumir subconjuntos diferentes de objetos em locais diferentes, é melhor evitar a persistência do estado globalmente.