Gerenciamento de sessão e estado no ASP.NET Core
Por Rick Anderson, Kirk Larkin e Diana LaRose
O HTTP é um protocolo sem estado. Por padrão, as solicitações HTTP são mensagens independentes que não conservam valores de usuário. Este artigo descreve várias abordagens para preservar dados do usuário entre solicitações.
Para obter diretrizes de gerenciamento de estado do Blazor, que adicionam ou substituem as diretrizes neste artigo, consulte Gerenciamento de estado do ASP.NET Core Blazor.
Gerenciamento de estado
O estado pode ser armazenado usando várias abordagens. Cada abordagem é descrita posteriormente neste artigo.
Abordagem de armazenamento | Mecanismo de armazenamento |
---|---|
Cookies | Cookies HTTP. Podem incluir dados armazenados usando o código do aplicativo do lado do servidor. |
Estado de sessão | Cookies HTTP e o código de aplicativo do lado do servidor |
TempData | Estado de sessão ou cookies HTTP |
Cadeias de consulta | Cadeias de caracteres de consulta HTTP |
Campos ocultos | Campos de formulário HTTP |
HttpContext.Items | Código do aplicativo do lado do servidor |
Cache | Código do aplicativo do lado do servidor |
SignalR/Blazor Server e gerenciamento de estado baseado em contexto HTTP
Os aplicativos de SignalR não devem usar o estado da sessão e outras abordagens de gerenciamento de estado que dependam de um contexto HTTP estável para armazenar informações. Os aplicativos SignalR podem armazenar o estado conforme a conexão em Context.Items
no hub . Para obter mais informações e abordagens alternativas de gerenciamento de estado para aplicativos Blazor Server, confira Gerenciamento de estado do Blazor no ASP.NET Core.
Cookies
Cookies armazenam dados entre solicitações. Como os cookies são enviados com cada solicitação, seu tamanho deve ser reduzido ao mínimo. Idealmente, somente um identificador deve ser armazenado em um cookie com os dados armazenados pelo aplicativo. A maioria dos navegadores restringe o tamanho do cookie a 4096 bytes. Somente um número limitado de cookies está disponível para cada domínio.
Uma vez que os cookies estão sujeitos à adulteração, eles devem ser validados pelo aplicativo. Os cookies podem ser excluídos por usuários e expirarem em clientes. No entanto, os cookies geralmente são a forma mais durável de persistência de dados no cliente.
Frequentemente, cookies são usados para personalização quando o conteúdo é personalizado para um usuário conhecido. O usuário é apenas identificado, e não autenticado, na maioria dos casos. O cookie pode armazenar o nome do usuário, o nome da conta ou a ID de usuário único (como um GUID). O cookie pode ser usado para acessar as configurações personalizadas do usuário, como cor preferida da tela de fundo do site.
Consulte o GDPR (Regulamento Geral sobre a Proteção de Dados) da União Europeia ao emitir cookies e lidar com questões de privacy. Para obter mais informações, veja Suporte ao RGPD (Regulamento Geral sobre a Proteção de Dados) no ASP.NET Core.
Estado de sessão
Estado de sessão é um cenário do ASP.NET Core para o armazenamento de dados de usuário enquanto o usuário procura um aplicativo Web. Estado de sessão usa um armazenamento mantido pelo aplicativo para que os dados persistam entre solicitações de um cliente. Os dados da sessão são apoiados por um cache e considerados dados efêmeros. O site deve continuar funcionando sem os dados da sessão. Os dados críticos do aplicativo devem ser armazenados no banco de dados do usuário e armazenados em cache na sessão apenas como uma otimização de desempenho.
A sessão não é compatível com os aplicativos SignalR porque um hub SignalR pode ser executado independente de um contexto HTTP. Por exemplo, isso pode ocorrer quando uma solicitação de sondagem longa é mantida aberta por um hub além do tempo de vida do contexto HTTP da solicitação.
O ASP.NET Core mantém o estado da sessão fornecendo um cookie ao cliente que contém uma ID de sessão. A ID da sessão do cookie:
- É enviado ao aplicativo com cada solicitação.
- É usado pelo aplicativo para buscar os dados da sessão.
Estado de sessão exibe os seguintes comportamentos:
- O cookie da sessão é específico do navegador. As sessões não são compartilhadas entre navegadores.
- Cookies da sessão são excluídos quando a sessão do navegador termina.
- Se um cookie for recebido de uma sessão expirada, será criada uma nova sessão que usa o mesmo cookie da sessão.
- As sessões vazias não são conservadas. A sessão precisa ter pelo menos um valor definido para persistir a sessão entre solicitações. Quando uma sessão não é mantida, uma nova ID de sessão é gerada para cada nova solicitação.
- O aplicativo mantém uma sessão por um tempo limitado após a última solicitação. O aplicativo define o tempo limite da sessão ou usa o valor padrão de 20 minutos. O estado da sessão é ideal para armazenar dados do usuário:
- Isso é específico de determinada sessão.
- Em que os dados não exigem armazenamento permanente entre sessões.
- Dados da sessão são excluídos quando a implementação ISession.Clear é chamada ou quando a sessão expira.
- Não há nenhum mecanismo padrão para informar o código do aplicativo de que um navegador cliente foi fechado ou quando o cookie da sessão foi excluído ou expirou no cliente.
- Os cookies de estado da sessão não são marcados como essenciais por padrão. O estado da sessão não é funcional, a menos que o acompanhamento seja permitido pelo visitante do site. Para obter mais informações, veja Suporte ao RGPD (Regulamento Geral sobre a Proteção de Dados) no ASP.NET Core.
- Observação: não há substituição para o recurso de sessão sem cookies do ASP.NET Framework porque ele é considerado inseguro e pode levar a ataques de fixação de sessão.
Aviso
Não armazene dados confidenciais no estado de sessão. O usuário não pode fechar o navegador e limpar o cookie da sessão. Alguns navegadores mantêm cookies de sessão válidos entre as janelas do navegador. Uma sessão pode não estar restrita a um único usuário. O próximo usuário pode continuar navegando pelo aplicativo com o mesmo cookie da sessão.
O provedor de cache na memória armazena dados de sessão na memória do servidor em que o aplicativo reside. Em um cenário de farm de servidores:
- Use sessões persistentes para vincular cada sessão a uma instância de aplicativo específico em um servidor individual. O Serviço de Aplicativo do Azure usa o ARR (Application Request Routing) para impor as sessões persistentes por padrão. Entretanto, sessões autoadesivas podem afetar a escalabilidade e complicar atualizações de aplicativos Web. Uma abordagem melhor é usar um Redis ou cache distribuído do SQL Server, que não requer sessões persistentes. Para obter mais informações, confira Cache distribuído no ASP.NET Core.
- O cookie da sessão é criptografado por meio de IDataProtector. A Proteção de Dados deve ser configurada corretamente para ler os cookies de sessão em cada computador. Para obter mais informações, confira Visão geral da Proteção de Dados do ASP.NET Core e Provedores de armazenamento de chaves.
Configurar o estado de sessão
O middleware para gerenciar o estado de sessão está incluído na estrutura. Para habilitar o middleware da sessão, Program.cs
deve conter:
- Qualquer um dos caches de memória de IDistributedCache. A implementação
IDistributedCache
é usada como um repositório de backup para a sessão. Para obter mais informações, confira Cache distribuído no ASP.NET Core. - Uma chamada para AddSession
- Uma chamada para UseSession
O código a seguir mostra como configurar o provedor de sessão na memória com uma implementação padrão na memória de IDistributedCache
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
O código anterior define um tempo limite curto para simplificar o teste.
A ordem do middleware é importante. Chame UseSession
depois de UseRouting
e antes de MapRazorPages
e MapDefaultControllerRoute
. Confira Ordenação de Middleware.
HttpContext.Session estará disponível depois que o estado de sessão for configurado.
HttpContext.Session
não pode ser acessado antes que UseSession
tenha sido chamado.
Não é possível criar uma nova sessão com um novo cookie da sessão depois que o aplicativo começou a gravar no fluxo de resposta. A exceção é registrada no log do servidor Web e não é exibida no navegador.
Carregue o estado de sessão de maneira assíncrona
O provedor de sessão padrão no ASP.NET Core somente carrega registros de sessão do repositório de backup IDistributedCache subjacente de forma assíncrona se o método ISession.LoadAsync for explicitamente chamado antes dos métodos TryGetValue, Set ou Remove. Se LoadAsync
não for chamado primeiro, o registro de sessão subjacente será carregado de maneira síncrona, o que pode incorrer em uma penalidade de desempenho em escala.
Para que os aplicativos imponham esse padrão, encapsule as implementações DistributedSessionStore e DistributedSession com versões que geram uma exceção quando o método LoadAsync
não é chamado antes de TryGetValue
, Set
ou Remove
. Registre as versões encapsuladas no contêiner de serviços.
Opções da sessão
Para substituir os padrões da sessão, use SessionOptions.
Opção | Descrição |
---|---|
Cookie | Determina as configurações usadas para criar o cookie. Name assume o padrão de SessionDefaults.CookieName (.AspNetCore.Session ). Path assume o padrão de SessionDefaults.CookiePath (/ ). SameSite assume o padrão de SameSiteMode.Lax (1 ). HttpOnly usa como padrão true . IsEssential usa como padrão false . |
IdleTimeout | O IdleTimeout indica por quanto tempo a sessão pode ficar ociosa antes de seu conteúdo ser abandonado. Cada acesso à sessão redefine o tempo limite. Essa configuração aplica-se somente ao conteúdo da sessão, não ao cookie. O padrão é de 20 minutos. |
IOTimeout | O tempo máximo permitido para carregar uma sessão do repositório ou para confirmá-la de volta para o repositório. Essa configuração pode se aplicar somente a operações assíncronas. Esse tempo limite pode ser desabilitado usando InfiniteTimeSpan. O padrão é 1 minuto. |
A sessão usa um cookie para rastrear e identificar solicitações de um único navegador. Por padrão, esse cookie se chama .AspNetCore.Session
, e usa um caminho de /
. Como o padrão do cookie não especifica um domínio, ele não fica disponível para o script do lado do cliente na página (porque HttpOnly assume o padrão de true
).
Para substituir os padrões da sessão do cookie, use SessionOptions:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.Cookie.Name = ".AdventureWorks.Session";
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.IsEssential = true;
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
O aplicativo usa a propriedade IdleTimeout para determinar por quanto tempo uma sessão pode ficar ociosa antes de seu conteúdo no cache do servidor ser abandonado. Essa propriedade é independente da expiração do cookie. Cada solicitação que passa pelo Middleware da Sessão redefine o tempo limite.
Estado de sessão é sem bloqueio. Se duas solicitações tentarem simultaneamente modificar o conteúdo de uma sessão, a última solicitação substituirá a primeira. Session
é implementado como uma sessão coerente, o que significa que todo o conteúdo é armazenado junto. Quando duas solicitações buscam modificar valores de sessão diferentes, a última solicitação pode substituir as alterações de sessão feitas pelo primeira.
Definir e obter valores de Session
O estado da sessão é acessado de uma Razor classe Pages PageModel ou classe MVC Controller com HttpContext.Session. Essa propriedade é uma implementação de ISession.
A implementação de ISession
fornece vários métodos de extensão para definir e recuperar valores de inteiro e cadeia de caracteres. Os novos métodos de extensão estão no namespace Microsoft.AspNetCore.Http.
Métodos de extensão ISession
:
- Get(ISession, String)
- GetInt32(ISession, String)
- GetString(ISession, String)
- SetInt32(ISession, String, Int32)
- SetString(ISession, String, String)
O exemplo abaixo recupera o valor da sessão para a chave IndexModel.SessionKeyName
(_Name
no aplicativo de exemplo) em uma página do Razor Pages:
@page
@using Microsoft.AspNetCore.Http
@model IndexModel
...
Name: @HttpContext.Session.GetString(IndexModel.SessionKeyName)
O exemplo a seguir mostra como definir e obter um número inteiro e uma cadeia de caracteres:
public class IndexModel : PageModel
{
public const string SessionKeyName = "_Name";
public const string SessionKeyAge = "_Age";
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
if (string.IsNullOrEmpty(HttpContext.Session.GetString(SessionKeyName)))
{
HttpContext.Session.SetString(SessionKeyName, "The Doctor");
HttpContext.Session.SetInt32(SessionKeyAge, 73);
}
var name = HttpContext.Session.GetString(SessionKeyName);
var age = HttpContext.Session.GetInt32(SessionKeyAge).ToString();
_logger.LogInformation("Session Name: {Name}", name);
_logger.LogInformation("Session Age: {Age}", age);
}
}
A marcação a seguir exibe os valores de sessão em uma Razor Page:
@page
@model PrivacyModel
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<div class="text-center">
<p><b>Name:</b> @HttpContext.Session.GetString("_Name");<b>Age:
</b> @HttpContext.Session.GetInt32("_Age").ToString()</p>
</div>
Todos os dados de sessão devem ser serializados para habilitar um cenário de cache distribuído, mesmo ao usar o cache na memória. Os serializadores de cadeia de caracteres e inteiros são fornecidos pelos métodos de extensão de ISession. Tipos complexos devem ser serializados pelo usuário por meio de outro mecanismo, como JSON.
Use o seguinte código de exemplo para serializar objetos:
public static class SessionExtensions
{
public static void Set<T>(this ISession session, string key, T value)
{
session.SetString(key, JsonSerializer.Serialize(value));
}
public static T? Get<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default : JsonSerializer.Deserialize<T>(value);
}
}
O exemplo abaixo mostra como definir e obter um objeto serializável com a classe SessionExtensions
:
using Microsoft.AspNetCore.Mvc.RazorPages;
using Web.Extensions; // SessionExtensions
namespace SessionSample.Pages
{
public class Index6Model : PageModel
{
const string SessionKeyTime = "_Time";
public string? SessionInfo_SessionTime { get; private set; }
private readonly ILogger<Index6Model> _logger;
public Index6Model(ILogger<Index6Model> logger)
{
_logger = logger;
}
public void OnGet()
{
var currentTime = DateTime.Now;
// Requires SessionExtensions from sample.
if (HttpContext.Session.Get<DateTime>(SessionKeyTime) == default)
{
HttpContext.Session.Set<DateTime>(SessionKeyTime, currentTime);
}
_logger.LogInformation("Current Time: {Time}", currentTime);
_logger.LogInformation("Session Time: {Time}",
HttpContext.Session.Get<DateTime>(SessionKeyTime));
}
}
}
Aviso
O armazenamento de um objeto ativo na sessão deve ser usado com cautela, pois há muitos problemas que podem ocorrer com objetos serializados. Para obter mais informações, confira As sessões devem ter permissão para armazenar objetos (dotnet/aspnetcore #18159).
TempData
O ASP.NET Core expõe as Razor Pages TempData ou Controller TempData. Essa propriedade armazena dados até que sejam lidos em outra solicitação. Os métodos Keep(String) e Peek(string) podem ser usados para examinar os dados sem exclusão no final da solicitação. Keep marca todos os itens no dicionário para conservação. TempData
é:
- Útil para redirecionamento nos casos em que os dados são exigidos para mais de uma única solicitação.
- Implementado por provedores de
TempData
usando cookies ou estados de sessão.
Exemplos de TempData
Considere a seguinte página que cria um cliente:
public class CreateModel : PageModel
{
private readonly RazorPagesContactsContext _context;
public CreateModel(RazorPagesContactsContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[TempData]
public string Message { get; set; }
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customer.Add(Customer);
await _context.SaveChangesAsync();
Message = $"Customer {Customer.Name} added";
return RedirectToPage("./IndexPeek");
}
}
A página abaixo exibe TempData["Message"]
:
@page
@model IndexModel
<h1>Peek Contacts</h1>
@{
if (TempData.Peek("Message") != null)
{
<h3>Message: @TempData.Peek("Message")</h3>
}
}
@*Content removed for brevity.*@
Na marcação anterior, no final da solicitação, TempData["Message"]
não é excluído porque Peek
é usado. A atualização da página exibe o conteúdo de TempData["Message"]
.
A marcação a seguir é semelhante ao código anterior, mas usa Keep
para preservar os dados no final da solicitação:
@page
@model IndexModel
<h1>Contacts Keep</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
TempData.Keep("Message");
}
@*Content removed for brevity.*@
A navegação entre as páginas IndexPeek e IndexKeep não excluirá TempData["Message"]
.
O código a seguir exibe TempData["Message"]
, mas, no final da solicitação, TempData["Message"]
é excluído:
@page
@model IndexModel
<h1>Index no Keep or Peek</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
}
@*Content removed for brevity.*@
Provedores de TempData
O provedor TempData baseado em cookie é usado por padrão para armazenar TempData em cookies.
Os dados do cookie são criptografados usando IDataProtector, codificados com Base64UrlTextEncodere agrupados. O tamanho máximo de cookie é menor que 4.096 bytes devido à criptografia e ao agrupamento. Os dados do cookie não são compactados porque a compactação de dados criptografados pode levar a problemas de segurança, como os ataques CRIME e BREACH. Para obter mais informações sobre o provedor TempData baseado em cookie, confira CookieTempDataProvider.
Escolha um provedor de TempData
Escolher um provedor de TempData envolve várias considerações, como:
- O aplicativo já usa estado de sessão? Se for o caso, o uso do provedor de TempData do estado de sessão não terá custos adicionais para o aplicativo além do tamanho dos dados.
- O aplicativo usa TempData apenas raramente para quantidades relativamente pequenas de dados, até 500 bytes? Em caso afirmativo, o provedor de TempData do cookie adiciona um pequeno custo a cada solicitação que transportar TempData. Caso contrário, o provedor de TempData do estado de sessão pode ser útil para evitar fazer viagens de ida e volta para uma grande quantidade de dados a cada solicitação até que TempData seja consumido.
- O aplicativo é executado em um farm de servidores em vários servidores? Nesse caso, não há nenhuma configuração adicional exigida para usar o provedor TempData de cookie fora da Proteção de Dados. Para obter mais informações, confira Visão geral da Proteção de Dados do ASP.NET Core e Provedores de armazenamento de chaves.
A maioria dos clientes da Web, como navegadores da Web, impõem limites quanto ao tamanho máximo de cada cookie e o número total de cookies. Ao usar o provedor TempData do cookie, verifique se o aplicativo não ultrapassará esses limites. Considere o tamanho total dos dados. Conta para aumento no tamanho de cookie devido à criptografia e ao agrupamento.
Configurar o provedor de TempData
O provedor de TempData baseado em cookie é habilitado por padrão.
Para habilitar o provedor TempData baseado em sessão, use o método de extensão AddSessionStateTempDataProvider. Somente uma chamada para AddSessionStateTempDataProvider
é necessária:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages()
.AddSessionStateTempDataProvider();
builder.Services.AddControllersWithViews()
.AddSessionStateTempDataProvider();
builder.Services.AddSession();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
Cadeias de consulta
É possível passar uma quantidade limitada de dados de uma solicitação para outra adicionando-a à cadeia de caracteres de consulta da nova solicitação. Isso é útil para capturar o estado de uma maneira persistente que permita que links com estado inserido sejam compartilhados por email ou por redes sociais. Uma vez que cadeias de consulta de URL são públicas, nunca use cadeias de consulta para dados confidenciais.
Além do compartilhamento não intencional, a inclusão de dados em cadeias de caracteres de consulta pode expor o aplicativo a ataques CSRF (Solicitação forjada entre sites). Todo estado de sessão preservado precisa se proteger contra ataques CSRF. Para obter mais informações, consulte Impedir ataques de XSRF/CSRF (solicitação intersite forjada) no ASP.NET Core.
Campos ocultos
Dados podem ser salvos em campos de formulário ocultos e postados novamente na solicitação seguinte. Isso é comum em formulários com várias páginas. Uma vez que o cliente potencialmente pode adulterar os dados, o aplicativo deve sempre revalidar os dados armazenados nos campos ocultos.
HttpContext.Items
A coleção HttpContext.Items é usada para armazenar dados durante o processamento de uma única solicitação. O conteúdo da coleção é descartado após uma solicitação ser processada. A coleção Items
costuma ser usada para permitir que componentes ou middleware se comuniquem quando operam em diferentes momentos durante uma solicitação e não têm nenhuma maneira direta de passar parâmetros.
No exemplo a seguir, middleware adiciona isVerified
à coleção Items
:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
ILogger logger = app.Logger;
app.Use(async (context, next) =>
{
// context.Items["isVerified"] is null
logger.LogInformation($"Before setting: Verified: {context.Items["isVerified"]}");
context.Items["isVerified"] = true;
await next.Invoke();
});
app.Use(async (context, next) =>
{
// context.Items["isVerified"] is true
logger.LogInformation($"Next: Verified: {context.Items["isVerified"]}");
await next.Invoke();
});
app.MapGet("/", async context =>
{
await context.Response.WriteAsync($"Verified: {context.Items["isVerified"]}");
});
app.Run();
Para middleware usado apenas em um único aplicativo, é improvável que o uso de uma chave de string
fixa cause uma colisão de chaves. No entanto, para evitar a possibilidade de uma colisão de chaves, um object
pode ser usado como uma chave de item. Essa abordagem é particularmente útil para middleware compartilhado entre aplicativos, além de ter a vantagem de eliminar o uso de cadeias de caracteres de chave no código. O exemplo abaixo mostra como usar uma chave de object
definida em uma classe de middleware:
public class HttpContextItemsMiddleware
{
private readonly RequestDelegate _next;
public static readonly object HttpContextItemsMiddlewareKey = new();
public HttpContextItemsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
httpContext.Items[HttpContextItemsMiddlewareKey] = "K-9";
await _next(httpContext);
}
}
public static class HttpContextItemsMiddlewareExtensions
{
public static IApplicationBuilder
UseHttpContextItemsMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<HttpContextItemsMiddleware>();
}
}
Outros códigos podem acessar o valor armazenado em HttpContext.Items
usando a chave exposta pela classe do middleware:
public class Index2Model : PageModel
{
private readonly ILogger<Index2Model> _logger;
public Index2Model(ILogger<Index2Model> logger)
{
_logger = logger;
}
public void OnGet()
{
HttpContext.Items
.TryGetValue(HttpContextItemsMiddleware.HttpContextItemsMiddlewareKey,
out var middlewareSetValue);
_logger.LogInformation("Middleware value {MV}",
middlewareSetValue?.ToString() ?? "Middleware value not set!");
}
}
Cache
O cache é uma maneira eficiente de armazenar e recuperar dados. O aplicativo pode controlar o tempo de vida de itens em cache. Para obter mais informações, confira Cache de resposta no ASP.NET Core.
Dados armazenados em cache não são associados uma solicitação, usuário ou sessão específico. Não armazene em cache dados específicos do usuário que possam ser recuperados por outras solicitações de usuário.
Para armazenar em cache dados de todo o aplicativo, confira Cache na memória no ASP.NET Core.
Verificando o estado de sessão
ISession.IsAvailable destina-se a verificar se há falhas transitórias. A chamada IsAvailable
antes da execução do middleware da sessão lança um InvalidOperationException
.
As bibliotecas que precisarem testar a disponibilidade da sessão poderão usar HttpContext.Features.Get<ISessionFeature>()?.Session != null
.
Erros comuns
"Não é possível resolver o serviço para o tipo 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' ao tentar ativar 'Microsoft.AspNetCore.Session.DistributedSessionStore'."
Isso costuma ser causado pela não configuração de pelo menos uma implementação de
IDistributedCache
. Para obter mais informações, confira Cache distribuído no ASP.NET Core e Cache na memória no ASP.NET Core.
Se o middleware de sessão não persistir uma sessão:
- O middleware registra a exceção e a solicitação continua normalmente.
- Isso leva a um comportamento imprevisível.
O middleware de sessão poderá falhar ao persistir uma sessão se o repositório de backup não estiver disponível. Por exemplo, um usuário armazena um carrinho de compras na sessão. O usuário adiciona um item ao carrinho, mas a confirmação falha. O aplicativo não sabe sobre a falha, assim, relata ao usuário que o item foi adicionado ao seu carrinho, o que não é verdade.
A abordagem recomendada para verificar se há erros é chamar await feature.Session.CommitAsync
quando o aplicativo tiver terminado de gravar na sessão. CommitAsync gerará uma exceção se o repositório de backup não estiver disponível. Se CommitAsync
falhar, o aplicativo poderá processar a exceção. LoadAsync gera sob as mesmas condições em que o armazenamento de dados não está disponível.
Recursos adicionais
Por Rick Anderson, Kirk Larkin e Diana LaRose
O HTTP é um protocolo sem estado. Por padrão, as solicitações HTTP são mensagens independentes que não conservam valores de usuário. Este artigo descreve várias abordagens para preservar dados do usuário entre solicitações.
Exibir ou baixar código de exemplo (como baixar)
Gerenciamento de estado
O estado pode ser armazenado usando várias abordagens. Cada abordagem é descrita posteriormente neste artigo.
Abordagem de armazenamento | Mecanismo de armazenamento |
---|---|
Cookies | Cookies HTTP. Podem incluir dados armazenados usando o código do aplicativo do lado do servidor. |
Estado de sessão | Cookies HTTP e o código de aplicativo do lado do servidor |
TempData | Estado de sessão ou cookies HTTP |
Cadeias de consulta | Cadeias de caracteres de consulta HTTP |
Campos ocultos | Campos de formulário HTTP |
HttpContext.Items | Código do aplicativo do lado do servidor |
Cache | Código do aplicativo do lado do servidor |
SignalR/Blazor Server e gerenciamento de estado baseado em contexto HTTP
Os aplicativos de SignalR não devem usar o estado da sessão e outras abordagens de gerenciamento de estado que dependam de um contexto HTTP estável para armazenar informações. Os aplicativos SignalR podem armazenar o estado conforme a conexão em Context.Items
no hub . Para obter mais informações e abordagens alternativas de gerenciamento de estado para aplicativos Blazor Server, confira Gerenciamento de estado do Blazor no ASP.NET Core.
Cookies
Cookies armazenam dados entre solicitações. Como os cookies são enviados com cada solicitação, seu tamanho deve ser reduzido ao mínimo. Idealmente, somente um identificador deve ser armazenado em um cookie com os dados armazenados pelo aplicativo. A maioria dos navegadores restringe o tamanho do cookie a 4096 bytes. Somente um número limitado de cookies está disponível para cada domínio.
Uma vez que os cookies estão sujeitos à adulteração, eles devem ser validados pelo aplicativo. Os cookies podem ser excluídos por usuários e expirarem em clientes. No entanto, os cookies geralmente são a forma mais durável de persistência de dados no cliente.
Frequentemente, cookies são usados para personalização quando o conteúdo é personalizado para um usuário conhecido. O usuário é apenas identificado, e não autenticado, na maioria dos casos. O cookie pode armazenar o nome do usuário, o nome da conta ou a ID de usuário único (como um GUID). O cookie pode ser usado para acessar as configurações personalizadas do usuário, como cor preferida da tela de fundo do site.
Consulte o GDPR (Regulamento Geral sobre a Proteção de Dados) da União Europeia ao emitir cookies e lidar com questões de privacy. Para obter mais informações, veja Suporte ao RGPD (Regulamento Geral sobre a Proteção de Dados) no ASP.NET Core.
Estado de sessão
Estado de sessão é um cenário do ASP.NET Core para o armazenamento de dados de usuário enquanto o usuário procura um aplicativo Web. Estado de sessão usa um armazenamento mantido pelo aplicativo para que os dados persistam entre solicitações de um cliente. Os dados da sessão são apoiados por um cache e considerados dados efêmeros. O site deve continuar funcionando sem os dados da sessão. Os dados críticos do aplicativo devem ser armazenados no banco de dados do usuário e armazenados em cache na sessão apenas como uma otimização de desempenho.
A sessão não é compatível com os aplicativos SignalR porque um hub SignalR pode ser executado independente de um contexto HTTP. Por exemplo, isso pode ocorrer quando uma solicitação de sondagem longa é mantida aberta por um hub além do tempo de vida do contexto HTTP da solicitação.
O ASP.NET Core mantém o estado da sessão fornecendo um cookie ao cliente que contém uma ID de sessão. A ID da sessão do cookie:
- É enviado ao aplicativo com cada solicitação.
- É usado pelo aplicativo para buscar os dados da sessão.
Estado de sessão exibe os seguintes comportamentos:
- O cookie da sessão é específico do navegador. As sessões não são compartilhadas entre navegadores.
- Cookies da sessão são excluídos quando a sessão do navegador termina.
- Se um cookie for recebido de uma sessão expirada, será criada uma nova sessão que usa o mesmo cookie da sessão.
- As sessões vazias não são conservadas. A sessão precisa ter pelo menos um valor definido para persistir a sessão entre solicitações. Quando uma sessão não é mantida, uma nova ID de sessão é gerada para cada nova solicitação.
- O aplicativo mantém uma sessão por um tempo limitado após a última solicitação. O aplicativo define o tempo limite da sessão ou usa o valor padrão de 20 minutos. O estado da sessão é ideal para armazenar dados do usuário:
- Isso é específico de determinada sessão.
- Em que os dados não exigem armazenamento permanente entre sessões.
- Dados da sessão são excluídos quando a implementação ISession.Clear é chamada ou quando a sessão expira.
- Não há nenhum mecanismo padrão para informar o código do aplicativo de que um navegador cliente foi fechado ou quando o cookie da sessão foi excluído ou expirou no cliente.
- Os cookies de estado da sessão não são marcados como essenciais por padrão. O estado da sessão não é funcional, a menos que o acompanhamento seja permitido pelo visitante do site. Para obter mais informações, veja Suporte ao RGPD (Regulamento Geral sobre a Proteção de Dados) no ASP.NET Core.
Aviso
Não armazene dados confidenciais no estado de sessão. O usuário não pode fechar o navegador e limpar o cookie da sessão. Alguns navegadores mantêm cookies de sessão válidos entre as janelas do navegador. Uma sessão pode não estar restrita a um único usuário. O próximo usuário pode continuar navegando pelo aplicativo com o mesmo cookie da sessão.
O provedor de cache na memória armazena dados de sessão na memória do servidor em que o aplicativo reside. Em um cenário de farm de servidores:
- Use sessões persistentes para vincular cada sessão a uma instância de aplicativo específico em um servidor individual. O Serviço de Aplicativo do Azure usa o ARR (Application Request Routing) para impor as sessões persistentes por padrão. Entretanto, sessões autoadesivas podem afetar a escalabilidade e complicar atualizações de aplicativos Web. Uma abordagem melhor é usar um Redis ou cache distribuído do SQL Server, que não requer sessões persistentes. Para obter mais informações, confira Cache distribuído no ASP.NET Core.
- O cookie da sessão é criptografado por meio de IDataProtector. A Proteção de Dados deve ser configurada corretamente para ler os cookies de sessão em cada computador. Para obter mais informações, confira Visão geral da Proteção de Dados do ASP.NET Core e Provedores de armazenamento de chaves.
Configurar o estado de sessão
O pacote Microsoft.AspNetCore.Session:
- É incluído implicitamente pela estrutura.
- Fornece middleware para gerenciar o estado de sessão.
Para habilitar o middleware da sessão, Startup
deve conter:
- Qualquer um dos caches de memória de IDistributedCache. A implementação
IDistributedCache
é usada como um repositório de backup para a sessão. Para obter mais informações, confira Cache distribuído no ASP.NET Core. - Uma chamada para AddSession em
ConfigureServices
. - Uma chamada para UseSession em
Configure
.
O código a seguir mostra como configurar o provedor de sessão na memória com uma implementação padrão na memória de IDistributedCache
:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
services.AddControllersWithViews();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}
O código anterior define um tempo limite curto para simplificar o teste.
A ordem do middleware é importante. Chame UseSession
depois de UseRouting
e antes de UseEndpoints
. Confira Ordenação de Middleware.
HttpContext.Session estará disponível depois que o estado de sessão for configurado.
HttpContext.Session
não pode ser acessado antes que UseSession
tenha sido chamado.
Não é possível criar uma nova sessão com um novo cookie da sessão depois que o aplicativo começou a gravar no fluxo de resposta. A exceção é registrada no log do servidor Web e não é exibida no navegador.
Carregue o estado de sessão de maneira assíncrona
O provedor de sessão padrão no ASP.NET Core somente carrega registros de sessão do repositório de backup IDistributedCache subjacente de forma assíncrona se o método ISession.LoadAsync for explicitamente chamado antes dos métodos TryGetValue, Set ou Remove. Se LoadAsync
não for chamado primeiro, o registro de sessão subjacente será carregado de maneira síncrona, o que pode incorrer em uma penalidade de desempenho em escala.
Para que os aplicativos imponham esse padrão, encapsule as implementações DistributedSessionStore e DistributedSession com versões que geram uma exceção quando o método LoadAsync
não é chamado antes de TryGetValue
, Set
ou Remove
. Registre as versões encapsuladas no contêiner de serviços.
Opções da sessão
Para substituir os padrões da sessão, use SessionOptions.
Opção | Descrição |
---|---|
Cookie | Determina as configurações usadas para criar o cookie. Name assume o padrão de SessionDefaults.CookieName (.AspNetCore.Session ). Path assume o padrão de SessionDefaults.CookiePath (/ ). SameSite assume o padrão de SameSiteMode.Lax (1 ). HttpOnly usa como padrão true . IsEssential usa como padrão false . |
IdleTimeout | O IdleTimeout indica por quanto tempo a sessão pode ficar ociosa antes de seu conteúdo ser abandonado. Cada acesso à sessão redefine o tempo limite. Essa configuração aplica-se somente ao conteúdo da sessão, não ao cookie. O padrão é de 20 minutos. |
IOTimeout | O tempo máximo permitido para carregar uma sessão do repositório ou para confirmá-la de volta para o repositório. Essa configuração pode se aplicar somente a operações assíncronas. Esse tempo limite pode ser desabilitado usando InfiniteTimeSpan. O padrão é 1 minuto. |
A sessão usa um cookie para rastrear e identificar solicitações de um único navegador. Por padrão, esse cookie se chama .AspNetCore.Session
, e usa um caminho de /
. Como o padrão do cookie não especifica um domínio, ele não fica disponível para o script do lado do cliente na página (porque HttpOnly assume o padrão de true
).
Para substituir os padrões da sessão do cookie, use SessionOptions:
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.Cookie.Name = ".AdventureWorks.Session";
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.IsEssential = true;
});
services.AddControllersWithViews();
services.AddRazorPages();
}
O aplicativo usa a propriedade IdleTimeout para determinar por quanto tempo uma sessão pode ficar ociosa antes de seu conteúdo no cache do servidor ser abandonado. Essa propriedade é independente da expiração do cookie. Cada solicitação que passa pelo Middleware da Sessão redefine o tempo limite.
Estado de sessão é sem bloqueio. Se duas solicitações tentarem simultaneamente modificar o conteúdo de uma sessão, a última solicitação substituirá a primeira. Session
é implementado como uma sessão coerente, o que significa que todo o conteúdo é armazenado junto. Quando duas solicitações buscam modificar valores de sessão diferentes, a última solicitação pode substituir as alterações de sessão feitas pelo primeira.
Definir e obter valores de Session
O estado da sessão é acessado de uma Razor classe Pages PageModel ou classe MVC Controller com HttpContext.Session. Essa propriedade é uma implementação de ISession.
A implementação de ISession
fornece vários métodos de extensão para definir e recuperar valores de inteiro e cadeia de caracteres. Os novos métodos de extensão estão no namespace Microsoft.AspNetCore.Http.
Métodos de extensão ISession
:
- Get(ISession, String)
- GetInt32(ISession, String)
- GetString(ISession, String)
- SetInt32(ISession, String, Int32)
- SetString(ISession, String, String)
O exemplo abaixo recupera o valor da sessão para a chave IndexModel.SessionKeyName
(_Name
no aplicativo de exemplo) em uma página do Razor Pages:
@page
@using Microsoft.AspNetCore.Http
@model IndexModel
...
Name: @HttpContext.Session.GetString(IndexModel.SessionKeyName)
O exemplo a seguir mostra como definir e obter um número inteiro e uma cadeia de caracteres:
public class IndexModel : PageModel
{
public const string SessionKeyName = "_Name";
public const string SessionKeyAge = "_Age";
const string SessionKeyTime = "_Time";
public string SessionInfo_Name { get; private set; }
public string SessionInfo_Age { get; private set; }
public string SessionInfo_CurrentTime { get; private set; }
public string SessionInfo_SessionTime { get; private set; }
public string SessionInfo_MiddlewareValue { get; private set; }
public void OnGet()
{
// Requires: using Microsoft.AspNetCore.Http;
if (string.IsNullOrEmpty(HttpContext.Session.GetString(SessionKeyName)))
{
HttpContext.Session.SetString(SessionKeyName, "The Doctor");
HttpContext.Session.SetInt32(SessionKeyAge, 773);
}
var name = HttpContext.Session.GetString(SessionKeyName);
var age = HttpContext.Session.GetInt32(SessionKeyAge);
Todos os dados de sessão devem ser serializados para habilitar um cenário de cache distribuído, mesmo ao usar o cache na memória. Os serializadores de cadeia de caracteres e inteiros são fornecidos pelos métodos de extensão de ISession. Tipos complexos devem ser serializados pelo usuário por meio de outro mecanismo, como JSON.
Use o seguinte código de exemplo para serializar objetos:
public static class SessionExtensions
{
public static void Set<T>(this ISession session, string key, T value)
{
session.SetString(key, JsonSerializer.Serialize(value));
}
public static T Get<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default : JsonSerializer.Deserialize<T>(value);
}
}
O exemplo abaixo mostra como definir e obter um objeto serializável com a classe SessionExtensions
:
// Requires SessionExtensions from sample download.
if (HttpContext.Session.Get<DateTime>(SessionKeyTime) == default)
{
HttpContext.Session.Set<DateTime>(SessionKeyTime, currentTime);
}
TempData
O ASP.NET Core expõe as Razor Pages TempData ou Controller TempData. Essa propriedade armazena dados até que sejam lidos em outra solicitação. Os métodos Keep(String) e Peek(string) podem ser usados para examinar os dados sem exclusão no final da solicitação. Keep marca todos os itens no dicionário para conservação. TempData
é:
- Útil para redirecionamento nos casos em que os dados são exigidos para mais de uma única solicitação.
- Implementado por provedores de
TempData
usando cookies ou estados de sessão.
Exemplos de TempData
Considere a seguinte página que cria um cliente:
public class CreateModel : PageModel
{
private readonly RazorPagesContactsContext _context;
public CreateModel(RazorPagesContactsContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[TempData]
public string Message { get; set; }
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customer.Add(Customer);
await _context.SaveChangesAsync();
Message = $"Customer {Customer.Name} added";
return RedirectToPage("./IndexPeek");
}
}
A página abaixo exibe TempData["Message"]
:
@page
@model IndexModel
<h1>Peek Contacts</h1>
@{
if (TempData.Peek("Message") != null)
{
<h3>Message: @TempData.Peek("Message")</h3>
}
}
@*Content removed for brevity.*@
Na marcação anterior, no final da solicitação, TempData["Message"]
não é excluído porque Peek
é usado. A atualização da página exibe o conteúdo de TempData["Message"]
.
A marcação a seguir é semelhante ao código anterior, mas usa Keep
para preservar os dados no final da solicitação:
@page
@model IndexModel
<h1>Contacts Keep</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
TempData.Keep("Message");
}
@*Content removed for brevity.*@
A navegação entre as páginas IndexPeek e IndexKeep não excluirá TempData["Message"]
.
O código a seguir exibe TempData["Message"]
, mas, no final da solicitação, TempData["Message"]
é excluído:
@page
@model IndexModel
<h1>Index no Keep or Peek</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
}
@*Content removed for brevity.*@
Provedores de TempData
O provedor TempData baseado em cookie é usado por padrão para armazenar TempData em cookies.
Os dados do cookie são criptografados usando IDataProtector, codificados com Base64UrlTextEncodere agrupados. O tamanho máximo de cookie é menor que 4.096 bytes devido à criptografia e ao agrupamento. Os dados do cookie não são compactados porque a compactação de dados criptografados pode levar a problemas de segurança, como os ataques CRIME e BREACH. Para obter mais informações sobre o provedor TempData baseado em cookie, confira CookieTempDataProvider.
Escolha um provedor de TempData
Escolher um provedor de TempData envolve várias considerações, como:
- O aplicativo já usa estado de sessão? Se for o caso, o uso do provedor de TempData do estado de sessão não terá custos adicionais para o aplicativo além do tamanho dos dados.
- O aplicativo usa TempData apenas raramente para quantidades relativamente pequenas de dados, até 500 bytes? Em caso afirmativo, o provedor de TempData do cookie adiciona um pequeno custo a cada solicitação que transportar TempData. Caso contrário, o provedor de TempData do estado de sessão pode ser útil para evitar fazer viagens de ida e volta para uma grande quantidade de dados a cada solicitação até que TempData seja consumido.
- O aplicativo é executado em um farm de servidores em vários servidores? Se for o caso, não será necessária nenhuma configuração adicional para usar o provedor TempData do cookie fora da Proteção de Dados (confira Visão geral da Proteção de Dados do ASP.NET Core e Provedores de armazenamento de chaves).
A maioria dos clientes da Web, como navegadores da Web, impõem limites quanto ao tamanho máximo de cada cookie e o número total de cookies. Ao usar o provedor TempData do cookie, verifique se o aplicativo não ultrapassará esses limites. Considere o tamanho total dos dados. Conta para aumento no tamanho de cookie devido à criptografia e ao agrupamento.
Configurar o provedor de TempData
O provedor de TempData baseado em cookie é habilitado por padrão.
Para habilitar o provedor TempData baseado em sessão, use o método de extensão AddSessionStateTempDataProvider. Somente uma chamada para AddSessionStateTempDataProvider
é necessária:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews()
.AddSessionStateTempDataProvider();
services.AddRazorPages()
.AddSessionStateTempDataProvider();
services.AddSession();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
Cadeias de consulta
É possível passar uma quantidade limitada de dados de uma solicitação para outra adicionando-a à cadeia de caracteres de consulta da nova solicitação. Isso é útil para capturar o estado de uma maneira persistente que permita que links com estado inserido sejam compartilhados por email ou por redes sociais. Uma vez que cadeias de consulta de URL são públicas, nunca use cadeias de consulta para dados confidenciais.
Além do compartilhamento não intencional, a inclusão de dados em cadeias de caracteres de consulta pode expor o aplicativo a ataques CSRF (Solicitação forjada entre sites). Todo estado de sessão preservado precisa se proteger contra ataques CSRF. Para obter mais informações, consulte Impedir ataques de XSRF/CSRF (solicitação intersite forjada) no ASP.NET Core.
Campos ocultos
Dados podem ser salvos em campos de formulário ocultos e postados novamente na solicitação seguinte. Isso é comum em formulários com várias páginas. Uma vez que o cliente potencialmente pode adulterar os dados, o aplicativo deve sempre revalidar os dados armazenados nos campos ocultos.
HttpContext.Items
A coleção HttpContext.Items é usada para armazenar dados durante o processamento de uma única solicitação. O conteúdo da coleção é descartado após uma solicitação ser processada. A coleção Items
costuma ser usada para permitir que componentes ou middleware se comuniquem quando operam em diferentes momentos durante uma solicitação e não têm nenhuma maneira direta de passar parâmetros.
No exemplo a seguir, middleware adiciona isVerified
à coleção Items
:
public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
app.UseRouting();
app.Use(async (context, next) =>
{
logger.LogInformation($"Before setting: Verified: {context.Items["isVerified"]}");
context.Items["isVerified"] = true;
await next.Invoke();
});
app.Use(async (context, next) =>
{
logger.LogInformation($"Next: Verified: {context.Items["isVerified"]}");
await next.Invoke();
});
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync($"Verified: {context.Items["isVerified"]}");
});
});
}
Para middleware usado apenas por um único aplicativo, chaves string
fixas são aceitáveis. Middleware compartilhado entre aplicativos deve usar chaves de objeto únicas para evitar colisões de chaves. O exemplo a seguir mostra como usar uma chave de objeto exclusiva definida em uma classe de middleware:
public class HttpContextItemsMiddleware
{
private readonly RequestDelegate _next;
public static readonly object HttpContextItemsMiddlewareKey = new Object();
public HttpContextItemsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
httpContext.Items[HttpContextItemsMiddlewareKey] = "K-9";
await _next(httpContext);
}
}
public static class HttpContextItemsMiddlewareExtensions
{
public static IApplicationBuilder
UseHttpContextItemsMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<HttpContextItemsMiddleware>();
}
}
Outros códigos podem acessar o valor armazenado em HttpContext.Items
usando a chave exposta pela classe do middleware:
HttpContext.Items
.TryGetValue(HttpContextItemsMiddleware.HttpContextItemsMiddlewareKey,
out var middlewareSetValue);
SessionInfo_MiddlewareValue =
middlewareSetValue?.ToString() ?? "Middleware value not set!";
Essa abordagem também tem a vantagem de eliminar o uso de cadeias de caracteres de chave no código.
Cache
O cache é uma maneira eficiente de armazenar e recuperar dados. O aplicativo pode controlar o tempo de vida de itens em cache. Para obter mais informações, confira Cache de resposta no ASP.NET Core.
Dados armazenados em cache não são associados uma solicitação, usuário ou sessão específico. Não armazene em cache dados específicos do usuário que possam ser recuperados por outras solicitações de usuário.
Para armazenar em cache dados de todo o aplicativo, confira Cache na memória no ASP.NET Core.
Erros comuns
"Não é possível resolver o serviço para o tipo 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' ao tentar ativar 'Microsoft.AspNetCore.Session.DistributedSessionStore'."
Isso costuma ser causado pela não configuração de pelo menos uma implementação de
IDistributedCache
. Para obter mais informações, confira Cache distribuído no ASP.NET Core e Cache na memória no ASP.NET Core.
Se o middleware de sessão não persistir uma sessão:
- O middleware registra a exceção e a solicitação continua normalmente.
- Isso leva a um comportamento imprevisível.
O middleware de sessão poderá falhar ao persistir uma sessão se o repositório de backup não estiver disponível. Por exemplo, um usuário armazena um carrinho de compras na sessão. O usuário adiciona um item ao carrinho, mas a confirmação falha. O aplicativo não sabe sobre a falha, assim, relata ao usuário que o item foi adicionado ao seu carrinho, o que não é verdade.
A abordagem recomendada para verificar se há erros é chamar await feature.Session.CommitAsync
quando o aplicativo tiver terminado de gravar na sessão. CommitAsync gerará uma exceção se o repositório de backup não estiver disponível. Se CommitAsync
falhar, o aplicativo poderá processar a exceção. LoadAsync gera sob as mesmas condições em que o armazenamento de dados não está disponível.