Administración del estado y la sesión en ASP.NET Core

Por Rick Anderson, Steve Smith y Diana LaRose

HTTP es un protocolo sin estado. De forma predeterminada, las solicitudes HTTP son mensajes independientes que no conservan los valores de usuario. En este artículo se describen varios enfoques para conservar los datos de usuario entre las solicitudes.

Administración de estado

El estado se puede almacenar mediante varios enfoques. Cada enfoque se describe más adelante en este artículo.

Enfoque de almacenamiento Mecanismo de almacenamiento
Cookies cookies HTTP. Pueden incluir los datos almacenados mediante el código de aplicación del lado servidor.
Estado de sesión cookies HTTP y código de aplicación del lado servidor
TempData cookies HTTP o estado de sesión
Cadenas de consulta Cadenas de consulta HTTP
Campos ocultos Campos de formularios HTTP
HttpContext.Items Código de aplicación del lado servidor
Caché Código de aplicación del lado servidor

SignalR/Blazor Server y administración de estado basado en contexto HTTP

Las aplicaciones de SignalR no deben usar el estado de sesión y otros enfoques de administración de estado que se basan en un contexto HTTP estable para almacenar información. Las aplicaciones SignalR pueden almacenarse por estado de conexión en Context.Items en el concentrador. Para obtener más información y enfoques alternativos de administración de estado para las aplicaciones de Blazor Server, consulte Administración de estado ASP.NET Core Blazor.

Cookies

Las Cookies almacenan datos de todas las solicitudes. Dado que las cookies se envían con cada solicitud, su tamaño debe reducirse al mínimo. Lo ideal es que en cada cookie se almacene un solo identificador con los datos almacenados por la aplicación. La mayoría de los exploradores restringen el tamaño de las cookies a 4096 bytes. Solo hay disponible un número limitado de cookies para cada dominio.

Como las cookies están expuestas a alteraciones, deben validarse por la aplicación. Los usuarios pueden eliminar las Cookies y estas pueden caducar en los clientes. Pero las cookies generalmente son la forma más duradera de persistencia de datos en el cliente.

Las Cookies suelen utilizarse para personalizar el contenido ofrecido a un usuario conocido. En la mayoría de los casos, el usuario solo se identifica y no se autentica. La cookie puede almacenar el nombre de usuario, el nombre de cuenta o el identificador único del usuario, por ejemplo, un GUID. Se puede usar la cookie para tener acceso a la configuración personalizada del usuario, como su color de fondo preferido del sitio web.

Consulte el Reglamento general de protección de datos (RGPD) de la Unión Europea al emitir cookies y tratar con casos relativos a la privacidad. Para obtener más información, vea Compatibilidad con el Reglamento general de protección de datos (RGPD) en ASP.NET Core.

Estado de sesión

El estado de sesión es un escenario de ASP.NET Core para el almacenamiento de datos de usuario mientras el usuario examina una aplicación web. El estado de sesión usa un almacén mantenido por la aplicación para conservar los datos en las solicitudes de un cliente. Los datos de sesión están respaldados por una memoria caché y se consideran datos efímeros. El sitio debería continuar funcionando correctamente sin los datos de sesión. Los datos críticos de aplicaciones deben almacenarse en la base de datos de usuario y almacenarse en caché en la sesión solo para optimizar el rendimiento.

La sesión no se admite en aplicaciones SignalR porque un SignalRconcentrador de se podría ejecutar con independencia de un contexto HTTP. Por ejemplo, esto puede ocurrir cuando un concentrador mantiene abierta una solicitud de sondeo larga más allá de la duración del contexto HTTP de la solicitud.

Para mantener el estado de sesión, ASP.NET Core proporciona una cookie al cliente que contiene un identificador de sesión. El identificador de sesión de la cookie:

  • Se envía a la aplicación con cada solicitud.
  • Lo usa la aplicación para capturar los datos de la sesión.

El estado de sesión muestra los siguientes comportamientos:

  • La cookie de sesión es específica de cada explorador. Las sesiones no se comparten entre los exploradores.
  • Las cookies de sesión se eliminan cuando finaliza la sesión del explorador.
  • Si se recibe una cookie de una sesión que ha expirado, se crea una nueva sesión que usa la misma cookie de sesión.
  • No se conservan las sesiones vacías. La sesión debe tener al menos un conjunto de valores para que se conserve en todas las solicitudes. Cuando una sesión no se conserva, se genera un nuevo identificador de sesión para cada nueva solicitud.
  • La aplicación conserva una sesión durante un tiempo limitado después de la última solicitud. La aplicación especifica un tiempo de espera de sesión o usa el valor predeterminado de 20 minutos. El estado de sesión es ideal para almacenar datos de usuario:
    • Que son específicos de una sesión determinada.
    • Que no necesitan conservarse de forma permanente en las sesiones.
  • Los datos de sesión se eliminan cuando se llama a la implementación ISession.Clear o cuando expira la sesión.
  • No hay ningún mecanismo predeterminado para informar al código de aplicación que se ha cerrado un explorador del cliente o cuando la cookie de sesión se elimina o caduca en el cliente.
  • Las cookies de estado de sesión no están marcadas como esenciales de forma predeterminada. De este modo, el estado de sesión no es funcional a menos que el visitante del sitio permita el seguimiento. Para obtener más información, vea Compatibilidad con el Reglamento general de protección de datos (RGPD) en ASP.NET Core.
  • Nota: No hay ningún reemplazo para la característica de sesión sin cookie de ASP.NET Framework porque se considera no segura y puede provocar ataques de fijación de sesión.

Advertencia

No almacene datos confidenciales en un estado de sesión. El usuario podría no cerrar el explorador y borrar la cookie de sesión. Algunos exploradores mantienen las cookies de sesión válidas en las ventanas del explorador. Es posible que una sesión no esté restringida a un único usuario. Así pues, el siguiente usuario podría continuar examinando la aplicación con la misma cookie de sesión.

El proveedor de caché en memoria almacena datos de sesión en la memoria del servidor donde reside la aplicación. En un escenario de granja de servidores:

Configurar el estado de sesión

El middleware para administrar el estado de sesión se incluye en el marco. Para habilitar el middleware de sesión, Program.cs debe contener:

El código siguiente muestra cómo configurar el proveedor de sesión en memoria con una implementación en memoria 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();

El código anterior establece un tiempo de expiración breve para simplificar la prueba.

El orden del middleware es importante. Llame a UseSession después de UseRouting y antes de MapRazorPages y MapDefaultControllerRoute. Consulte Orden del middleware.

HttpContext.Session está disponible después de configurar el estado de sesión.

No se puede acceder a HttpContext.Session antes de llamar a UseSession.

No se puede crear una nueva sesión con una cookie de sesión nueva después de que la aplicación haya empezado a escribir en la secuencia de respuesta. La excepción se registra en el registro del servidor web y no se muestra en el explorador.

Cargar de forma asincrónica el estado de sesión

El proveedor de sesión predeterminado de ASP.NET Core carga de manera asincrónica registros de sesión del almacén de respaldo IDistributedCache subyacente solo si se llama al método ISession.LoadAsync de forma explícita antes que a los métodos TryGetValue, Set o Remove. Si primero no se llama a LoadAsync, el registro de sesión subyacente se carga de forma sincrónica, lo que podría conllevar una disminución del rendimiento al escalar.

Para que las aplicaciones impongan este patrón, encapsule las implementaciones DistributedSessionStore y DistributedSession con versiones que inicien una excepción si no se llama al método LoadAsync antes que a TryGetValue, Set o Remove. Registre las versiones ajustadas en el contenedor de servicios.

Opciones de sesión

Para reemplazar los valores predeterminados de la sesión, use SessionOptions.

Opción Descripción
Cookie Determina la configuración usada para crear la cookie. Name se establece de forma predeterminada en SessionDefaults.CookieName (.AspNetCore.Session). Path se establece de forma predeterminada en SessionDefaults.CookiePath (/). SameSite se establece de forma predeterminada en SameSiteMode.Lax (1). HttpOnly se establece de forma predeterminada en true. IsEssential se establece de forma predeterminada en false.
IdleTimeout IdleTimeout indica cuánto tiempo puede estar inactiva la sesión antes de que se abandone su contenido. Cada acceso a la sesión restablece el tiempo de espera. Este valor solo es aplicable al contenido de la sesión, no a la cookie. El valor predeterminado es de 20 minutos.
IOTimeout El período máximo de tiempo permitido para cargar una sesión del almacén o devolverla a él. Este valor solo es aplicable a las operaciones asincrónicas. Este tiempo de espera se puede deshabilitar mediante InfiniteTimeSpan. El valor predeterminado es 1 minuto.

La sesión utiliza una cookie para realizar el seguimiento de las solicitudes emitidas por un solo explorador e identificarlas. De manera predeterminada, esta cookie se denomina .AspNetCore.Session y usa una ruta de acceso de /. Dado que el valor predeterminado de la cookie no especifica un dominio, no estará disponible para el script de cliente en la página (porque HttpOnly se establece de forma predeterminada en true).

Para reemplazar los valores predeterminados de la sesión de cookies, 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();

La aplicación usa la propiedad IdleTimeout para determinar el tiempo que una sesión puede estar inactiva antes de que se abandone su contenido en la caché del servidor. Esta propiedad es independiente de la expiración de la cookie. Cada solicitud que se pasa a través del middleware de sesión restablece el tiempo de espera.

El estado de sesión es no realiza bloqueo. Si dos solicitudes intentan modificar el contenido de una sesión simultáneamente, la última solicitud reemplaza a la primera. Session se implementa como una sesión coherente, lo que significa que todo el contenido se almacena junto. Cuando dos solicitudes buscan modificar diferentes valores de sesión, la última solicitud podría reemplazar los cambios de sesión realizados por la primera.

Establecer y obtener valores de Session

Se tiene acceso al estado de sesión desde la clase PageModel de Razor Pages o desde la clase Controller de MVC con HttpContext.Session. Esta propiedad es una implementación de ISession.

La implementación ISession proporciona varios métodos de extensión para establecer y recuperar valores de cadena y enteros. Los nuevos métodos de extensión se encuentran en el espacio de nombres Microsoft.AspNetCore.Http.

Métodos de extensión ISession:

En el ejemplo siguiente se recupera el valor de sesión de la clave IndexModel.SessionKeyName (_Name en la aplicación de ejemplo) de una página de Razor Pages:

@page
@using Microsoft.AspNetCore.Http
@model IndexModel

...

Name: @HttpContext.Session.GetString(IndexModel.SessionKeyName)

En el ejemplo siguiente se muestra cómo establecer y obtener un entero y una cadena:

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);
    }
}

El marcado siguiente muestra los valores de sesión en una instancia de Razor Pages:

@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 los datos de sesión se deben serializar para habilitar un escenario de caché distribuida, incluso cuando se usa la caché en memoria. Los métodos de extensión de ISession proporcionan serializadores de cadenas y enteros. El usuario debe serializar los tipos complejos mediante otro mecanismo, como JSON.

Use el siguiente código de ejemplo 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);
    }
}

En el ejemplo siguiente se muestra cómo establecer y obtener un objeto serializable con la clase 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));

        }
    }
}

Advertencia

El almacenamiento de un objeto dinámico en la sesión debe usarse con precaución, ya que hay muchos problemas que pueden producirse con objetos serializados. Para obtener más información, consulte Se debe permitir a las sesiones almacenar objetos (dotnet/aspnetcore #18159).

TempData

ASP.NET Core expone TempData de Razor Pages o TempData del controlador. Esta propiedad almacena datos hasta que se leen en otra solicitud. Los métodos Keep(String) y Peek(string) se pueden usar para examinar los datos sin eliminarlos al final de la solicitud. Keep marca todos los elementos del diccionario para su retención. TempData es:

  • Útil para el redireccionamiento cuando se necesitan los datos de más de una única solicitud.
  • Implementada por los proveedores de TempData mediante cookies o el estado de sesión.

Ejemplos de TempData

Considere la siguiente página que crea un 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");
    }
}

La siguiente página muestra TempData["Message"]:

@page
@model IndexModel

<h1>Peek Contacts</h1>

@{
    if (TempData.Peek("Message") != null)
    {
        <h3>Message: @TempData.Peek("Message")</h3>
    }
}

@*Content removed for brevity.*@

En el marcado anterior, al final de la solicitud, TempData["Message"]no se elimina porque se usa Peek. Al actualizar la página, se muestra el contenido de TempData["Message"].

El marcado siguiente es similar al código anterior, pero usa Keep para conservar los datos al final de la solicitud:

@page
@model IndexModel

<h1>Contacts Keep</h1>

@{
    if (TempData["Message"] != null)
    {
        <h3>Message: @TempData["Message"]</h3>
    }
    TempData.Keep("Message");
}

@*Content removed for brevity.*@

La navegación entre las páginas IndexPeek y IndexKeep no eliminará TempData["Message"].

El código siguiente muestra TempData["Message"], pero al final de la solicitud, se elimina TempData["Message"]:

@page
@model IndexModel

<h1>Index no Keep or Peek</h1>

@{
    if (TempData["Message"] != null)
    {
        <h3>Message: @TempData["Message"]</h3>
    }
}

@*Content removed for brevity.*@

Proveedores de TempData

El proveedor TempData basado en cookies se usa de forma predeterminada para almacenar TempData en cookies.

Los datos de cookies se cifran mediante IDataProtector, codificados con Base64UrlTextEncoder para posteriormente fragmentarse. El tamaño máximo de la cookie es inferior a 4096 bytes debido al cifrado y a la fragmentación. Los datos de cookie no se comprimen porque la compresión de datos cifrados puede provocar problemas de seguridad como los ataques CRIME y BREACH. Para más información sobre el proveedor TempData basado en cookies, consulte CookieTempDataProvider.

Elegir un proveedor TempData

Elegir un proveedor TempData implica una serie de consideraciones:

  • ¿La aplicación ya usa el estado de sesión? Si es así, el uso del proveedor TempData de estado de sesión no tiene costo adicional para la aplicación (excepto en el tamaño de los datos).
  • ¿La aplicación usa TempData con moderación, solo para cantidades relativamente pequeñas de datos (hasta 500 bytes)? Si es así, el proveedor TempData de cookies agrega un pequeño costo a cada solicitud que transporta TempData. De lo contrario, el proveedor TempData de estado de sesión puede ser beneficioso para evitar que una gran cantidad de datos hagan un recorrido de ida y vuelta en cada solicitud hasta que se consuma TempData.
  • ¿La aplicación se ejecuta en una granja de servidores en varios servidores? Si es así, no es necesaria ninguna configuración adicional para usar el proveedor de TempData de cookies además de la protección de datos. Para más información, consulte Información general de la protección de datos en ASP.NET Core y Proveedores de almacenamiento de claves.

La mayoría de los clientes web (por ejemplo, los exploradores web) aplican límites en el tamaño máximo de cada cookie, el número total de cookies o ambos. Cuando use el proveedor TempData de cookies, asegúrese de que la aplicación no supera esos límites. Tenga en cuenta el tamaño total de los datos. Cuenta para los aumentos de tamaño de cookie debidos a la fragmentación y el cifrado.

Configurar el proveedor TempData

El proveedor TempData basado en cookies está habilitado de forma predeterminada.

Para habilitar el proveedor TempData basado en sesión, use el método de extensión AddSessionStateTempDataProvider. Solo se requiere una llamada a AddSessionStateTempDataProvider:

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();

Cadenas de consulta

Se puede pasar una cantidad limitada de datos de una solicitud a otra si agrega los datos a la cadena de consulta de la solicitud nueva. Esto es útil para capturar el estado de una forma persistente que permita que los vínculos con estado insertado se compartan a través del correo electrónico o las redes sociales. Dado que las cadenas de consulta de direcciones URL son públicas, nunca use las cadenas de consulta para datos confidenciales.

Además del uso compartido no intencionado, la inclusión de datos en cadenas de consulta puede exponer la aplicación a ataques de falsificación de solicitud entre sitios (CSRF). Cualquier estado de sesión conservado debe protegerse contra los ataques CSRF. Para obtener más información, vea Prevención de ataques de falsificación de solicitud entre sitios (XSRF/CSRF) en ASP.NET Core.

Campos ocultos

Los datos pueden guardarse en campos ocultos de formulario e incluirse de nuevo en la siguiente solicitud. Esto es habitual en los formularios de varias páginas. Dado que el cliente puede llegar a alterar los datos, la aplicación siempre debe revalidar los datos almacenados en campos ocultos.

HttpContext.Items

La colección HttpContext.Items se usa para almacenar los datos al procesar una única solicitud. El contenido de la colección se descarta después de procesar una solicitud. A menudo se usa la colección Items para permitir que los componentes o el middleware se comuniquen cuando operan en distintos puntos en el tiempo durante una solicitud y no pueden pasarse parámetros de forma directa.

En el ejemplo siguiente, el middleware agrega isVerified a la colección 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();

En el caso del middleware que solo se usa en una sola aplicación, es poco probable que usar una clave string fija produzca una colisión de claves. Sin embargo, para evitar la posibilidad de una colisión de claves por completo, se puede usar object como clave de elemento. Este enfoque es especialmente útil para el middleware que se comparte entre aplicaciones, y también tiene la ventaja de eliminar el uso de cadenas de clave en el código. En el ejemplo siguiente se muestra cómo usar una clave object definida en una clase 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>();
    }
}

Otro código puede tener acceso al valor almacenado en HttpContext.Items con la clave que expone la clase de 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!");
    }
}

instancias y claves

El almacenamiento en caché es una manera eficaz de almacenar y recuperar datos. La aplicación puede controlar la duración de los elementos almacenados en caché. Para más información, consulte Almacenamiento en caché de respuestas en ASP.NET Core.

Los datos almacenados en caché no están asociados a una solicitud, usuario o sesión específicos. Procure no almacenar en caché datos específicos de usuario que se puedan recuperar mediante las solicitudes de otros usuarios.

Para almacenar en caché datos de toda la aplicación, consulte Caché en memoria en ASP.NET Core.

Comprobación del estado de sesión

ISession.IsAvailable tiene la finalidad de comprobar si existen errores transitorios. Llamar a IsAvailable antes de que se ejecute el middleware de sesión produce una InvalidOperationException.

Las bibliotecas que necesitan comprobar la disponibilidad de la sesión pueden usar HttpContext.Features.Get<ISessionFeature>()?.Session != null.

Errores comunes

Si el middleware de sesión no puede conservar una sesión:

  • El middleware registra la excepción y la solicitud se procesa con normalidad.
  • Esto provoca un comportamiento imprevisible.

El middleware de sesión puede no conservar una sesión si la memoria auxiliar no está disponible. Por ejemplo, un usuario almacena un carro de la compra en la sesión. El usuario agrega un elemento al carro, pero se produce un error en la confirmación. La aplicación no se percata del error y notifica al usuario que el elemento se ha agregado al carro, lo cual no es cierto.

El enfoque recomendado para comprobar si hay errores es llamar a await feature.Session.CommitAsync cuando la aplicación haya terminado de escribir en la sesión. CommitAsync produce una excepción si la memoria auxiliar no está disponible. Si CommitAsync produce un error, la aplicación puede procesar la excepción. LoadAsync se produce en las mismas condiciones cuando el almacén de datos no está disponible.

Recursos adicionales

Vea o descargue el código de ejemplo (cómo descargarlo)

Hospedaje de ASP.NET Core en una granja de servidores web

Por Rick Anderson, Steve Smith y Diana LaRose

HTTP es un protocolo sin estado. De forma predeterminada, las solicitudes HTTP son mensajes independientes que no conservan los valores de usuario. En este artículo se describen varios enfoques para conservar los datos de usuario entre las solicitudes.

Vea o descargue el código de ejemplo (cómo descargarlo)

Administración de estado

El estado se puede almacenar mediante varios enfoques. Cada enfoque se describe más adelante en este artículo.

Enfoque de almacenamiento Mecanismo de almacenamiento
Cookies cookies HTTP. Pueden incluir los datos almacenados mediante el código de aplicación del lado servidor.
Estado de sesión cookies HTTP y código de aplicación del lado servidor
TempData cookies HTTP o estado de sesión
Cadenas de consulta Cadenas de consulta HTTP
Campos ocultos Campos de formularios HTTP
HttpContext.Items Código de aplicación del lado servidor
Caché Código de aplicación del lado servidor

SignalR/Blazor Server y administración de estado basado en contexto HTTP

Las aplicaciones de SignalR no deben usar el estado de sesión y otros enfoques de administración de estado que se basan en un contexto HTTP estable para almacenar información. Las aplicaciones SignalR pueden almacenarse por estado de conexión en Context.Items en el concentrador. Para obtener más información y enfoques alternativos de administración de estado para las aplicaciones de Blazor Server, consulte Administración de estado ASP.NET Core Blazor.

Cookies

Las Cookies almacenan datos de todas las solicitudes. Dado que las cookies se envían con cada solicitud, su tamaño debe reducirse al mínimo. Lo ideal es que en cada cookie se almacene un solo identificador con los datos almacenados por la aplicación. La mayoría de los exploradores restringen el tamaño de las cookies a 4096 bytes. Solo hay disponible un número limitado de cookies para cada dominio.

Como las cookies están expuestas a alteraciones, deben validarse por la aplicación. Los usuarios pueden eliminar las Cookies y estas pueden caducar en los clientes. Pero las cookies generalmente son la forma más duradera de persistencia de datos en el cliente.

Las Cookies suelen utilizarse para personalizar el contenido ofrecido a un usuario conocido. En la mayoría de los casos, el usuario solo se identifica y no se autentica. La cookie puede almacenar el nombre de usuario, el nombre de cuenta o el identificador único del usuario, por ejemplo, un GUID. Se puede usar la cookie para tener acceso a la configuración personalizada del usuario, como su color de fondo preferido del sitio web.

Consulte el Reglamento general de protección de datos (RGPD) de la Unión Europea al emitir cookies y tratar con casos relativos a la privacidad. Para obtener más información, vea Compatibilidad con el Reglamento general de protección de datos (RGPD) en ASP.NET Core.

Estado de sesión

El estado de sesión es un escenario de ASP.NET Core para el almacenamiento de datos de usuario mientras el usuario examina una aplicación web. El estado de sesión usa un almacén mantenido por la aplicación para conservar los datos en las solicitudes de un cliente. Los datos de sesión están respaldados por una memoria caché y se consideran datos efímeros. El sitio debería continuar funcionando correctamente sin los datos de sesión. Los datos críticos de aplicaciones deben almacenarse en la base de datos de usuario y almacenarse en caché en la sesión solo para optimizar el rendimiento.

La sesión no se admite en aplicaciones SignalR porque un SignalRconcentrador de se podría ejecutar con independencia de un contexto HTTP. Por ejemplo, esto puede ocurrir cuando un concentrador mantiene abierta una solicitud de sondeo larga más allá de la duración del contexto HTTP de la solicitud.

Para mantener el estado de sesión, ASP.NET Core proporciona una cookie al cliente que contiene un identificador de sesión. El identificador de sesión de la cookie:

  • Se envía a la aplicación con cada solicitud.
  • Lo usa la aplicación para capturar los datos de la sesión.

El estado de sesión muestra los siguientes comportamientos:

  • La cookie de sesión es específica de cada explorador. Las sesiones no se comparten entre los exploradores.
  • Las cookies de sesión se eliminan cuando finaliza la sesión del explorador.
  • Si se recibe una cookie de una sesión que ha expirado, se crea una nueva sesión que usa la misma cookie de sesión.
  • No se conservan las sesiones vacías. La sesión debe tener al menos un conjunto de valores para que se conserve en todas las solicitudes. Cuando una sesión no se conserva, se genera un nuevo identificador de sesión para cada nueva solicitud.
  • La aplicación conserva una sesión durante un tiempo limitado después de la última solicitud. La aplicación especifica un tiempo de espera de sesión o usa el valor predeterminado de 20 minutos. El estado de sesión es ideal para almacenar datos de usuario:
    • Que son específicos de una sesión determinada.
    • Que no necesitan conservarse de forma permanente en las sesiones.
  • Los datos de sesión se eliminan cuando se llama a la implementación ISession.Clear o cuando expira la sesión.
  • No hay ningún mecanismo predeterminado para informar al código de aplicación que se ha cerrado un explorador del cliente o cuando la cookie de sesión se elimina o caduca en el cliente.
  • Las cookies de estado de sesión no están marcadas como esenciales de forma predeterminada. De este modo, el estado de sesión no es funcional a menos que el visitante del sitio permita el seguimiento. Para obtener más información, vea Compatibilidad con el Reglamento general de protección de datos (RGPD) en ASP.NET Core.

Advertencia

No almacene datos confidenciales en un estado de sesión. El usuario podría no cerrar el explorador y borrar la cookie de sesión. Algunos exploradores mantienen las cookies de sesión válidas en las ventanas del explorador. Es posible que una sesión no esté restringida a un único usuario. Así pues, el siguiente usuario podría continuar examinando la aplicación con la misma cookie de sesión.

El proveedor de caché en memoria almacena datos de sesión en la memoria del servidor donde reside la aplicación. En un escenario de granja de servidores:

Configurar el estado de sesión

El paquete Microsoft.AspNetCore.Session:

  • Está incluido implícitamente en el marco.
  • Proporciona middleware para administrar el estado de sesión.

Para habilitar el middleware de sesión, Startup debe contener:

El código siguiente muestra cómo configurar el proveedor de sesión en memoria con una implementación en memoria 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();
        });
    }
}

El código anterior establece un tiempo de expiración breve para simplificar la prueba.

El orden del middleware es importante. Llame a UseSession después de llamar a UseRouting y antes de la llamada a UseEndpoints. Consulte Orden del middleware.

HttpContext.Session está disponible después de configurar el estado de sesión.

No se puede acceder a HttpContext.Session antes de llamar a UseSession.

No se puede crear una nueva sesión con una cookie de sesión nueva después de que la aplicación haya empezado a escribir en la secuencia de respuesta. La excepción se registra en el registro del servidor web y no se muestra en el explorador.

Cargar de forma asincrónica el estado de sesión

El proveedor de sesión predeterminado de ASP.NET Core carga de manera asincrónica registros de sesión del almacén de respaldo IDistributedCache subyacente solo si se llama al método ISession.LoadAsync de forma explícita antes que a los métodos TryGetValue, Set o Remove. Si primero no se llama a LoadAsync, el registro de sesión subyacente se carga de forma sincrónica, lo que podría conllevar una disminución del rendimiento al escalar.

Para que las aplicaciones impongan este patrón, encapsule las implementaciones DistributedSessionStore y DistributedSession con versiones que inicien una excepción si no se llama al método LoadAsync antes que a TryGetValue, Set o Remove. Registre las versiones ajustadas en el contenedor de servicios.

Opciones de sesión

Para reemplazar los valores predeterminados de la sesión, use SessionOptions.

Opción Descripción
Cookie Determina la configuración usada para crear la cookie. Name se establece de forma predeterminada en SessionDefaults.CookieName (.AspNetCore.Session). Path se establece de forma predeterminada en SessionDefaults.CookiePath (/). SameSite se establece de forma predeterminada en SameSiteMode.Lax (1). HttpOnly se establece de forma predeterminada en true. IsEssential se establece de forma predeterminada en false.
IdleTimeout IdleTimeout indica cuánto tiempo puede estar inactiva la sesión antes de que se abandone su contenido. Cada acceso a la sesión restablece el tiempo de espera. Este valor solo es aplicable al contenido de la sesión, no a la cookie. El valor predeterminado es de 20 minutos.
IOTimeout El período máximo de tiempo permitido para cargar una sesión del almacén o devolverla a él. Este valor solo es aplicable a las operaciones asincrónicas. Este tiempo de espera se puede deshabilitar mediante InfiniteTimeSpan. El valor predeterminado es 1 minuto.

La sesión utiliza una cookie para realizar el seguimiento de las solicitudes emitidas por un solo explorador e identificarlas. De manera predeterminada, esta cookie se denomina .AspNetCore.Session y usa una ruta de acceso de /. Dado que el valor predeterminado de la cookie no especifica un dominio, no estará disponible para el script de cliente en la página (porque HttpOnly se establece de forma predeterminada en true).

Para reemplazar los valores predeterminados de la sesión de cookies, 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();
}

La aplicación usa la propiedad IdleTimeout para determinar el tiempo que una sesión puede estar inactiva antes de que se abandone su contenido en la caché del servidor. Esta propiedad es independiente de la expiración de la cookie. Cada solicitud que se pasa a través del middleware de sesión restablece el tiempo de espera.

El estado de sesión es no realiza bloqueo. Si dos solicitudes intentan modificar el contenido de una sesión simultáneamente, la última solicitud reemplaza a la primera. Session se implementa como una sesión coherente, lo que significa que todo el contenido se almacena junto. Cuando dos solicitudes buscan modificar diferentes valores de sesión, la última solicitud podría reemplazar los cambios de sesión realizados por la primera.

Establecer y obtener valores de Session

Se tiene acceso al estado de sesión desde la clase PageModel de Razor Pages o desde la clase Controller de MVC con HttpContext.Session. Esta propiedad es una implementación de ISession.

La implementación ISession proporciona varios métodos de extensión para establecer y recuperar valores de cadena y enteros. Los nuevos métodos de extensión se encuentran en el espacio de nombres Microsoft.AspNetCore.Http.

Métodos de extensión ISession:

En el ejemplo siguiente se recupera el valor de sesión de la clave IndexModel.SessionKeyName (_Name en la aplicación de ejemplo) de una página de Razor Pages:

@page
@using Microsoft.AspNetCore.Http
@model IndexModel

...

Name: @HttpContext.Session.GetString(IndexModel.SessionKeyName)

En el ejemplo siguiente se muestra cómo establecer y obtener un entero y una cadena:

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 los datos de sesión se deben serializar para habilitar un escenario de caché distribuida, incluso cuando se usa la caché en memoria. Los métodos de extensión de ISession proporcionan serializadores de cadenas y enteros. El usuario debe serializar los tipos complejos mediante otro mecanismo, como JSON.

Use el siguiente código de ejemplo 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);
    }
}

En el ejemplo siguiente se muestra cómo establecer y obtener un objeto serializable con la clase SessionExtensions:

// Requires SessionExtensions from sample download.
if (HttpContext.Session.Get<DateTime>(SessionKeyTime) == default)
{
    HttpContext.Session.Set<DateTime>(SessionKeyTime, currentTime);
}

TempData

ASP.NET Core expone TempData de Razor Pages o TempData del controlador. Esta propiedad almacena datos hasta que se leen en otra solicitud. Los métodos Keep(String) y Peek(string) se pueden usar para examinar los datos sin eliminarlos al final de la solicitud. Keep marca todos los elementos del diccionario para su retención. TempData es:

  • Útil para el redireccionamiento cuando se necesitan los datos de más de una única solicitud.
  • Implementada por los proveedores de TempData mediante cookies o el estado de sesión.

Ejemplos de TempData

Considere la siguiente página que crea un 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");
    }
}

La siguiente página muestra TempData["Message"]:

@page
@model IndexModel

<h1>Peek Contacts</h1>

@{
    if (TempData.Peek("Message") != null)
    {
        <h3>Message: @TempData.Peek("Message")</h3>
    }
}

@*Content removed for brevity.*@

En el marcado anterior, al final de la solicitud, TempData["Message"]no se elimina porque se usa Peek. Al actualizar la página, se muestra el contenido de TempData["Message"].

El marcado siguiente es similar al código anterior, pero usa Keep para conservar los datos al final de la solicitud:

@page
@model IndexModel

<h1>Contacts Keep</h1>

@{
    if (TempData["Message"] != null)
    {
        <h3>Message: @TempData["Message"]</h3>
    }
    TempData.Keep("Message");
}

@*Content removed for brevity.*@

La navegación entre las páginas IndexPeek y IndexKeep no eliminará TempData["Message"].

El código siguiente muestra TempData["Message"], pero al final de la solicitud, se elimina TempData["Message"]:

@page
@model IndexModel

<h1>Index no Keep or Peek</h1>

@{
    if (TempData["Message"] != null)
    {
        <h3>Message: @TempData["Message"]</h3>
    }
}

@*Content removed for brevity.*@

Proveedores de TempData

El proveedor TempData basado en cookies se usa de forma predeterminada para almacenar TempData en cookies.

Los datos de cookies se cifran mediante IDataProtector, codificados con Base64UrlTextEncoder para posteriormente fragmentarse. El tamaño máximo de la cookie es inferior a 4096 bytes debido al cifrado y a la fragmentación. Los datos de cookie no se comprimen porque la compresión de datos cifrados puede provocar problemas de seguridad como los ataques CRIME y BREACH. Para más información sobre el proveedor TempData basado en cookies, consulte CookieTempDataProvider.

Elegir un proveedor TempData

Elegir un proveedor TempData implica una serie de consideraciones:

  • ¿La aplicación ya usa el estado de sesión? Si es así, el uso del proveedor TempData de estado de sesión no tiene costo adicional para la aplicación (excepto en el tamaño de los datos).
  • ¿La aplicación usa TempData con moderación, solo para cantidades relativamente pequeñas de datos (hasta 500 bytes)? Si es así, el proveedor TempData de cookies agrega un pequeño costo a cada solicitud que transporta TempData. De lo contrario, el proveedor TempData de estado de sesión puede ser beneficioso para evitar que una gran cantidad de datos hagan un recorrido de ida y vuelta en cada solicitud hasta que se consuma TempData.
  • ¿La aplicación se ejecuta en una granja de servidores en varios servidores? Si es así, no es necesaria ninguna configuración adicional para usar el proveedor de TempData de cookie además de la protección de datos (consulte Información general de la protección de datos de ASP.NET Core y Proveedores de almacenamiento de claves).

La mayoría de los clientes web (por ejemplo, los exploradores web) aplican límites en el tamaño máximo de cada cookie, el número total de cookies o ambos. Cuando use el proveedor TempData de cookies, asegúrese de que la aplicación no supera esos límites. Tenga en cuenta el tamaño total de los datos. Cuenta para los aumentos de tamaño de cookie debidos a la fragmentación y el cifrado.

Configurar el proveedor TempData

El proveedor TempData basado en cookies está habilitado de forma predeterminada.

Para habilitar el proveedor TempData basado en sesión, use el método de extensión AddSessionStateTempDataProvider. Solo se requiere una llamada a AddSessionStateTempDataProvider:

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();
    });
}

Cadenas de consulta

Se puede pasar una cantidad limitada de datos de una solicitud a otra si agrega los datos a la cadena de consulta de la solicitud nueva. Esto es útil para capturar el estado de una forma persistente que permita que los vínculos con estado insertado se compartan a través del correo electrónico o las redes sociales. Dado que las cadenas de consulta de direcciones URL son públicas, nunca use las cadenas de consulta para datos confidenciales.

Además del uso compartido no intencionado, la inclusión de datos en cadenas de consulta puede exponer la aplicación a ataques de falsificación de solicitud entre sitios (CSRF). Cualquier estado de sesión conservado debe protegerse contra los ataques CSRF. Para obtener más información, vea Prevención de ataques de falsificación de solicitud entre sitios (XSRF/CSRF) en ASP.NET Core.

Campos ocultos

Los datos pueden guardarse en campos ocultos de formulario e incluirse de nuevo en la siguiente solicitud. Esto es habitual en los formularios de varias páginas. Dado que el cliente puede llegar a alterar los datos, la aplicación siempre debe revalidar los datos almacenados en campos ocultos.

HttpContext.Items

La colección HttpContext.Items se usa para almacenar los datos al procesar una única solicitud. El contenido de la colección se descarta después de procesar una solicitud. A menudo se usa la colección Items para permitir que los componentes o el middleware se comuniquen cuando operan en distintos puntos en el tiempo durante una solicitud y no pueden pasarse parámetros de forma directa.

En el ejemplo siguiente, el middleware agrega isVerified a la colección 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"]}");
        });
    });
}

Si el middleware solo se usa en una única aplicación, se aceptan claves string fijas. El middleware compartido entre aplicaciones debería usar claves de objeto únicas para evitar conflictos de clave. En el ejemplo siguiente se muestra cómo usar una clave de objeto única definida en una clase 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>();
    }
}

Otro código puede tener acceso al valor almacenado en HttpContext.Items con la clave que expone la clase de middleware:

HttpContext.Items
    .TryGetValue(HttpContextItemsMiddleware.HttpContextItemsMiddlewareKey, 
        out var middlewareSetValue);
SessionInfo_MiddlewareValue = 
    middlewareSetValue?.ToString() ?? "Middleware value not set!";

Este enfoque también tiene la ventaja de eliminar el uso de cadenas de claves en el código.

instancias y claves

El almacenamiento en caché es una manera eficaz de almacenar y recuperar datos. La aplicación puede controlar la duración de los elementos almacenados en caché. Para más información, consulte Almacenamiento en caché de respuestas en ASP.NET Core.

Los datos almacenados en caché no están asociados a una solicitud, usuario o sesión específicos. Procure no almacenar en caché datos específicos de usuario que se puedan recuperar mediante las solicitudes de otros usuarios.

Para almacenar en caché datos de toda la aplicación, consulte Caché en memoria en ASP.NET Core.

Errores comunes

Si el middleware de sesión no puede conservar una sesión:

  • El middleware registra la excepción y la solicitud se procesa con normalidad.
  • Esto provoca un comportamiento imprevisible.

El middleware de sesión puede no conservar una sesión si la memoria auxiliar no está disponible. Por ejemplo, un usuario almacena un carro de la compra en la sesión. El usuario agrega un elemento al carro, pero se produce un error en la confirmación. La aplicación no se percata del error y notifica al usuario que el elemento se ha agregado al carro, lo cual no es cierto.

El enfoque recomendado para comprobar si hay errores es llamar a await feature.Session.CommitAsync cuando la aplicación haya terminado de escribir en la sesión. CommitAsync produce una excepción si la memoria auxiliar no está disponible. Si CommitAsync produce un error, la aplicación puede procesar la excepción. LoadAsync se produce en las mismas condiciones cuando el almacén de datos no está disponible.

Recursos adicionales

Hospedaje de ASP.NET Core en una granja de servidores web