Gestione di sessioni e stato in ASP.NET Core

Di Rick Anderson, Kirk Larkin e Diana LaRose

HTTP è un protocollo senza stato. Per impostazione predefinita, le richieste HTTP sono messaggi indipendenti che non mantengono i valori utente. Questo articolo descrive diversi approcci per mantenere i dati utente tra le richieste.

Gestione dello stato

Lo stato può essere archiviato usando diversi approcci. Ogni approccio viene descritto più avanti in questo articolo.

Approccio con risorsa di archiviazione Meccanismo di archiviazione
Cookies HTTP cookies. Può includere i dati archiviati usando il codice dell'app sul lato server.
Stato della sessione Codice dell'app http cookiee lato server
TempData Stato di sessione o http cookie
Stringhe di query Stringhe di query HTTP
Campi nascosti Campi dei form HTTP
HttpContext.Items Codice app lato server
Cache Codice app lato server

SignalR/Blazor Server e la gestione dello stato basata sul contesto HTTP

SignalR Le app non devono usare lo stato della sessione e altri approcci di gestione dello stato che si basano su un contesto HTTP stabile per archiviare le informazioni. SignalRle app possono archiviare lo stato per ogni connessione nell'hubContext.Items. Per altre informazioni e approcci alternativi per la gestione dello stato per Blazor Server le app, vedere ASP.NET Gestione dello stato coreBlazor.

Cookies

Cookies archivia i dati tra le richieste. Poiché cookievengono inviati con ogni richiesta, le dimensioni devono essere mantenute al minimo. Idealmente, solo un identificatore deve essere archiviato in un cookie oggetto con i dati archiviati dall'app. La maggior parte dei browser limita cookie le dimensioni a 4096 byte. Per ogni dominio sono disponibili solo un numero limitato di cookies.

Poiché cookiesono soggetti a manomissioni, devono essere convalidati dall'app. Cookies può essere eliminato dagli utenti e scadere nei client. Tuttavia, cookies è in genere la forma più durevole di persistenza dei dati nel client.

Cookies viene spesso usato per la personalizzazione, in cui il contenuto viene personalizzato per un utente noto. L'utente viene solo identificato e non autenticato nella maggior parte dei casi. cookie può archiviare il nome dell'utente, il nome dell'account o l'ID utente univoco, ad esempio un GUID. Può cookie essere usato per accedere alle impostazioni personalizzate dell'utente, ad esempio il colore di sfondo del sito Web preferito.

Vedere le normative generali sulla protezione dei dati dell'Unione europea (GDPR) quando si emettono cookiee gestiscono problemi di privacy. Per altre informazioni, vedere il supporto per il Regolamento generale sulla protezione dei dati in ASP.NET Core.

Stato della sessione

Lo stato della sessione è uno scenario di ASP.NET Core per l'archiviazione dei dati utente mentre l'utente visualizza un'app Web. Lo stato della sessione usa un archivio gestito dall'app per rendere persistenti i dati tra le richieste provenienti da un client. I dati della sessione sono supportati da una cache e considerati dati temporanei. Il sito deve continuare a funzionare senza i dati della sessione. I dati critici dell'applicazione devono essere archiviati nel database utente e memorizzati nella cache della sessione solo al fine di ottimizzare le prestazioni.

La sessione non è supportata nelle SignalR app perché un SignalR hub può essere eseguito indipendentemente da un contesto HTTP. Ad esempio, ciò può verificarsi quando una lunga richiesta di polling viene mantenuta aperta da un hub oltre la durata del contesto HTTP della richiesta.

ASP.NET Core mantiene lo stato della sessione fornendo un cookie al client che contiene un ID sessione. ID cookie sessione:

  • Viene inviato all'app con ogni richiesta.
  • Viene usato dall'app per recuperare i dati della sessione.

Lo stato della sessione presenta i comportamenti seguenti:

  • La sessione cookie è specifica del browser. Le sessioni non vengono condivise tra i browser.
  • Le sessioni cookievengono eliminate al termine della sessione del browser.
  • Se viene ricevuto un oggetto cookie per una sessione scaduta, viene creata una nuova sessione che usa la stessa sessione cookie.
  • Le sessioni vuote non vengono mantenute. La sessione deve avere almeno un valore impostato per rendere persistente la sessione tra le richieste. Se una sessione non viene conservata, viene generato un nuovo ID sessione per ogni nuova richiesta.
  • L'app conserva una sessione per un periodo di tempo limitato dopo l'ultima richiesta. L'app imposta il timeout della sessione o usa il valore predefinito, pari a 20 minuti. Lo stato della sessione è ideale per l'archiviazione dei dati utente:
    • Questo è specifico di una sessione specifica.
    • Dove i dati non richiedono l'archiviazione permanente tra le sessioni.
  • I dati della sessione vengono eliminati quando viene chiamata l'implementazione ISession.Clear o alla scadenza della sessione.
  • Non esiste alcun meccanismo predefinito per informare il codice dell'app che un browser client è stato chiuso o quando la sessione cookie viene eliminata o scaduta nel client.
  • Per impostazione predefinita, lo stato cookiedella sessione non è contrassegnato come essenziale. Lo stato della sessione non è funzionale a meno che il rilevamento non sia consentito dal visitatore del sito. Per altre informazioni, vedere il supporto per il Regolamento generale sulla protezione dei dati in ASP.NET Core.
  • Nota: non esiste alcuna sostituzione per la cookiefunzionalità di sessione inferiore da ASP.NET Framework perché è considerata non sicura e può causare attacchi di correzione della sessione.

Avviso

Evitare di archiviare dati sensibili nello stato della sessione. L'utente potrebbe non chiudere il browser e cancellare la sessione cookie. Alcuni browser mantengono la sessione cookievalida tra le finestre del browser. Una sessione potrebbe non essere limitata a un singolo utente. L'utente successivo potrebbe continuare a esplorare l'app con la stessa sessione cookie.

Il provider di cache in memoria archivia i dati della sessione nella memoria del server in cui si trova l'app. In uno scenario di server farm:

  • Usare sessioni permanenti per associare ogni sessione a un'istanza specifica dell'app in un singolo server. Il Servizio app di Azure usa il modulo Application Request Routing (ARR) per applicare le sessioni permanenti per impostazione predefinita. Tuttavia le sessioni permanenti possono compromettere la scalabilità e complicare gli aggiornamenti delle app Web. Un approccio migliore è usare la cache distribuita Redis o SQL Server, che non richiede sessioni permanenti. Per altre informazioni, vedere Memorizzazione nella cache distribuita in ASP.NET Core.
  • La sessione cookie viene crittografata tramite IDataProtector. La protezione dei dati deve essere configurata correttamente per leggere le sessioni cookiein ogni computer. Per altre informazioni, vedere ASP.NET Panoramica della protezione dei dati di base e provider di archiviazione delle chiavi.

Configurare lo stato della sessione

Il middleware per la gestione dello stato della sessione è incluso nel framework. Per abilitare il middleware della sessione, Program.cs deve contenere:

Il codice seguente indica come configurare il provider della sessione in memoria con un'implementazione in memoria predefinita di 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();

Il codice precedente imposta un breve timeout per semplificare il test.

L'ordine del middleware è importante. Chiamare UseSession dopo UseRouting e prima MapRazorPages e MapDefaultControllerRoute . Vedere Ordinamento del middleware.

HttpContext. Session è disponibile dopo che è stato configurato lo stato della sessione.

Non è possibile accedere a HttpContext.Session prima di chiamare UseSession.

Non è possibile creare una nuova sessione con una nuova sessione cookie dopo che l'app ha iniziato a scrivere nel flusso di risposta. L'eccezione viene registrata nel log del server Web e non è visualizzata nel browser.

Caricare lo stato della sessione in modo asincrono

Il provider di sessione predefinito in ASP.NET Core carica i record di sessione dall'archivio di backup sottostante IDistributedCache in modo asincrono solo se il ISession.LoadAsync metodo viene chiamato in modo esplicito prima dei TryGetValuemetodi , Seto Remove . Se LoadAsync non viene chiamato per primo, il record di sessione sottostante viene caricato in modo sincrono e ciò può compromettere le prestazioni su larga scala.

Per applicare questo modello alle app, eseguire il wrapping delle DistributedSessionStore implementazioni e DistributedSession con versioni che generano un'eccezione se il LoadAsync metodo non viene chiamato prima TryGetValuedi , Seto Remove. Registrare le versioni con wrapping nel contenitore di servizi.

Opzioni di sessione

Per eseguire l'override delle impostazioni predefinite della sessione, usare SessionOptions.

Opzione Descrizione
Cookie Determina le impostazioni utilizzate per creare l'oggetto cookie. Name il valore predefinito è SessionDefaults.CookieName (.AspNetCore.Session). Path il valore predefinito è SessionDefaults.CookiePath (/). SameSite il valore predefinito è SameSiteMode.Lax (1). Il valore predefinito di HttpOnly è true. Il valore predefinito di IsEssential è false.
IdleTimeout Il valore IdleTimeout indica quanto tempo la sessione può rimanere inattiva prima che il relativo contenuto venga abbandonato. Ogni accesso alla sessione reimposta il timeout. Questa impostazione si applica solo al contenuto della sessione, non a cookie. Il valore predefinito è 20 minuti.
IOTimeout L'intervallo di tempo massimo consentito per caricare una sessione dall'archivio o per eseguirne il commit nell'archivio. Questa impostazione può essere applicabile solo alle operazioni asincrone. Questo timeout può essere disabilitato tramite InfiniteTimeSpan. Il valore predefinito è 1 minuto.

La sessione usa un cookie oggetto per tenere traccia e identificare le richieste da un singolo browser. Per impostazione predefinita, questo cookie nome è .AspNetCore.Sessione usa un percorso di /. Poiché il cookie valore predefinito non specifica un dominio, non viene reso disponibile per lo script sul lato client nella pagina (perché HttpOnly per impostazione predefinita è true).

Per eseguire l'override delle impostazioni predefinite cookie della sessione, usare 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();

L'app usa la IdleTimeout proprietà per determinare per quanto tempo una sessione può essere inattiva prima che il relativo contenuto nella cache del server venga abbandonato. Questa proprietà è indipendente dalla cookie scadenza. Ogni richiesta che passa attraverso il middleware di sessione reimposta il timeout.

Lo stato della sessione è non di blocco. Se due richieste tentano simultaneamente di modificare il contenuto di una sessione, l'ultima richiesta sostituisce la prima. Session viene implementata come sessione coerente, ovvero tutto il contenuto viene archiviato insieme. Quando due richieste tentano di modificare diversi valori della sessione, l'ultima richiesta può sostituire le modifiche apportate alla sessione dalla prima.

Impostare e ottenere i valori della sessione

Lo stato della sessione è accessibile da una classe Pages PageModel o da una Razor classe MVC Controller con HttpContext.Session. Questa proprietà è un'implementazione ISession .

L'implementazione ISession offre diversi metodi di estensione per impostare e recuperare i valori interi e stringa. I metodi di estensione si trovano nello spazio dei Microsoft.AspNetCore.Http nomi .

Metodi di estensione ISession:

L'esempio seguente recupera il valore della sessione per la IndexModel.SessionKeyName chiave (_Name nell'app di esempio) in una Razor pagina Pages:

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

...

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

L'esempio seguente illustra come impostare e ottenere un intero e una stringa:

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

Nel markup seguente vengono visualizzati i valori di sessione in una Razor pagina:

@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>


Tutti i dati della sessione devono essere serializzati per abilitare uno scenario di cache distribuita, anche quando si usa la cache in memoria. I serializzatori stringa e integer vengono forniti dai metodi di estensione di ISession. I tipi complessi devono essere serializzati dall'utente usando un altro meccanismo, ad esempio JSON.

Usare il codice di esempio seguente per serializzare gli oggetti:

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

L'esempio seguente illustra come impostare e ottenere un oggetto serializzabile con la SessionExtensions classe :

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

        }
    }
}

Avviso

L'archiviazione di un oggetto attivo nella sessione deve essere usata con cautela, perché esistono molti problemi che possono verificarsi con oggetti serializzati. Per altre informazioni, vedere Sessioni che devono essere autorizzate a archiviare oggetti (dotnet/aspnetcore #18159)..

TempData

ASP.NET Core espone Razor Pages TempData o Controller TempData. Questa proprietà archivia i dati finché non viene letto in un'altra richiesta. I metodi Keep(String) e Peek(string) possono essere usati per esaminare i dati senza eliminazione alla fine della richiesta. Mantieni contrassegna tutti gli elementi nel dizionario per la conservazione. TempData è:

  • Utile per il reindirizzamento quando i dati sono necessari per più di una singola richiesta.
  • Implementato dai TempData provider che usano cookielo stato di sessione o s.

Esempi di TempData

Si consideri la pagina seguente che 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");
    }
}

Nella pagina seguente viene visualizzato TempData["Message"]:

@page
@model IndexModel

<h1>Peek Contacts</h1>

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

@*Content removed for brevity.*@

Nel markup precedente, alla fine della richiesta, TempData["Message"] non viene eliminato perché Peek viene usato. L'aggiornamento della pagina visualizza il contenuto di TempData["Message"].

Il markup seguente è simile al codice precedente, ma usa Keep per mantenere i dati alla fine della richiesta:

@page
@model IndexModel

<h1>Contacts Keep</h1>

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

@*Content removed for brevity.*@

Lo spostamento tra le pagine IndexPeek e IndexKeep non eliminerà TempData["Message"].

Il codice seguente visualizza TempData["Message"], ma alla fine della richiesta viene TempData["Message"] eliminato:

@page
@model IndexModel

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

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

@*Content removed for brevity.*@

Provider TempData

Il cookieprovider TempData basato su viene usato per impostazione predefinita per archiviare TempData in cookies.

I cookie dati vengono crittografati usando IDataProtector, codificati con Base64UrlTextEncoder, quindi in blocchi. La dimensione massima cookie è inferiore a 4096 byte a causa della crittografia e della suddivisione in blocchi. I cookie dati non vengono compressi perché la compressione dei dati crittografati può causare problemi di sicurezza, ad CRIME esempio gli attacchi e BREACH . Per altre informazioni sul cookieprovider TempData basato su , vedere CookieTempDataProvider.

Scegliere un provider TempData

La scelta di un provider TempData implica diverse considerazioni, tra cui:

  • L'app utilizza già lo stato sessione? In tal caso, l'uso del provider TempData dello stato della sessione non comporta costi aggiuntivi per l'app oltre le dimensioni dei dati.
  • L'app usa TempData solo con moderazione per quantità relativamente ridotte di dati, fino a 500 byte? In tal caso, il cookie provider TempData aggiunge un costo ridotto a ogni richiesta che contiene TempData. Se no, il provider TempData con stato sessione può essere utile per evitare sequenze di andata e ritorno di grandi quantità di dati in ogni richiesta fino a quando il contenuto TempData non viene consumato.
  • L'app viene eseguita in un server farm in più server? In tal caso, non è necessaria alcuna configurazione aggiuntiva per l'uso del cookie provider TempData all'esterno della protezione dei dati. Per altre informazioni, vedere ASP.NET Panoramica della protezione dei dati di base e provider di archiviazione delle chiavi.

La maggior parte dei client Web, ad esempio i Web browser, applica limiti alle dimensioni massime di ognuno cookie e al numero totale di cookies. Quando si usa il cookie provider TempData, verificare che l'app non superi questi limiti. Considerare la dimensione totale dei dati. Tenere conto dell'aumento delle dimensioni a causa della crittografia e della suddivisione in cookie blocchi.

Configurare il provider TempData

Il cookieprovider TempData basato su è abilitato per impostazione predefinita.

Per abilitare il provider TempData basato su sessione, usare il AddSessionStateTempDataProvider metodo di estensione. È necessaria una sola chiamata 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();

Stringhe di query

È possibile passare una quantità limitata di dati da una richiesta a un'altra aggiungendo i dati alla stringa di query della nuova richiesta. Questo è utile per l'acquisizione dello stato con una modalità persistente, che consente la condivisione dei collegamenti con stato incorporato tramite posta elettronica o social network. Poiché le stringhe di query dell'URL sono pubbliche, non usare mai le stringhe di query per i dati sensibili.

Oltre alla condivisione non intenzionale, inclusi i dati nelle stringhe di query, l'app può esporre l'app agli attacchi CSRF (Cross-Site Request Forgery). Qualsiasi stato della sessione mantenuto deve proteggere dagli attacchi CSRF. Per altre informazioni, vedere Prevenire attacchi tramite richieste intersito false (XSRF/CSRF) in ASP.NET Core.

Campi nascosti

I dati possono essere salvati in campi modulo nascosti e pubblicati di nuovo nella richiesta successiva. Questo si verifica spesso nei moduli a più pagine. Poiché il client potenzialmente può alterare i dati, l'app deve sempre ripetere la convalida dei dati archiviati nei campi nascosti.

HttpContext.Items

La HttpContext.Items raccolta viene utilizzata per archiviare i dati durante l'elaborazione di una singola richiesta. Il contenuto della raccolta viene rimosso al termine dell'elaborazione della richiesta. La raccolta Items spesso viene usata per consentire ai componenti o al middleware di comunicare quando operano in momenti diversi durante una richiesta e non è disponibile un metodo diretto per passare i parametri.

Nell'esempio seguente il middleware aggiunge isVerified alla Items raccolta:

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

Per il middleware usato solo in una singola app, è improbabile che l'uso di una chiave fissa string causi un conflitto di tasti. Tuttavia, per evitare completamente la possibilità di un conflitto di tasti, un object oggetto può essere usato come chiave dell'elemento. Questo approccio è particolarmente utile per il middleware condiviso tra le app e offre anche il vantaggio di eliminare l'uso di stringhe chiave nel codice. L'esempio seguente illustra come usare una object chiave definita in una classe 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>();
    }
}

Altri elementi di codice possono accedere al valore archiviato in HttpContext.Items usando la chiave esposta dalla classe 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

La memorizzazione nella cache è un modo efficiente per archiviare e recuperare dati. L'app può controllare la durata degli elementi della cache. Per altre informazioni, vedere Memorizzazione nella cache delle risposte in ASP.NET Core.

I dati memorizzati nella cache non sono associati a una richiesta, un utente o una sessione specifici. Non memorizzare nella cache i dati specifici dell'utente che possono essere recuperati da altre richieste utente.

Per memorizzare nella cache i dati a livello di applicazione, vedere Cache in memoria in ASP.NET Core.

Controllo dello stato della sessione

ISession.IsAvailable è progettato per verificare la presenza di errori temporanei. La chiamata IsAvailable prima dell'esecuzione del middleware di sessione genera un'eccezione InvalidOperationException.

Le librerie che devono testare la disponibilità della sessione possono usare HttpContext.Features.Get<ISessionFeature>()?.Session != null.

Errori comuni

  • "Unable to resolve service for type 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' while attempting to activate 'Microsoft.AspNetCore.Session.DistributedSessionStore'." (Risoluzione servizio non riuscita per il tipo 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' durante il tentativo di attivazione di 'Microsoft.AspNetCore.Session.DistributedSessionStore').

    Ciò è in genere causato dalla mancata configurazione di almeno un'implementazione IDistributedCache . Per altre informazioni, vedere Memorizzazione nella cache distribuita in ASP.NET Core e cache in memoria in ASP.NET Core.

Se il middleware della sessione non riesce a rendere persistente una sessione:

  • Il middleware registra l'eccezione e la richiesta continua normalmente.
  • Ciò provoca un comportamento imprevedibile.

Il middleware della sessione può non rendere persistente una sessione se l'archivio di backup non è disponibile. Ad esempio, un utente archivia un carrello acquisti nella sessione. L'utente aggiunge un articolo al carrello ma il commit ha esito negativo. L'app non riconosce l'errore e segnala all'utente che l'articolo è stato aggiunto al carrello anche se non è vero.

L'approccio consigliato per verificare la presenza di errori consiste nel chiamare await feature.Session.CommitAsync quando l'app ha completato la scrittura nella sessione. CommitAsync genera un'eccezione se l'archivio di backup non è disponibile. Se CommitAsync ha esito negativo, l'app è in grado di elaborare l'eccezione. LoadAsync genera le stesse condizioni quando l'archivio dati non è disponibile.

Risorse aggiuntive

Visualizzare o scaricare il codice di esempio (procedura per il download)

Ospitare ASP.NET Core in una web farm

Di Rick Anderson, Kirk Larkin e Diana LaRose

HTTP è un protocollo senza stato. Per impostazione predefinita, le richieste HTTP sono messaggi indipendenti che non mantengono i valori utente. Questo articolo descrive diversi approcci per mantenere i dati utente tra le richieste.

Visualizzare o scaricare il codice di esempio (procedura per il download)

Gestione dello stato

Lo stato può essere archiviato usando diversi approcci. Ogni approccio viene descritto più avanti in questo articolo.

Approccio con risorsa di archiviazione Meccanismo di archiviazione
Cookies HTTP cookies. Può includere i dati archiviati usando il codice dell'app sul lato server.
Stato della sessione Codice dell'app http cookiee lato server
TempData Stato di sessione o http cookie
Stringhe di query Stringhe di query HTTP
Campi nascosti Campi dei form HTTP
HttpContext.Items Codice app lato server
Cache Codice app lato server

SignalR/Blazor Server e la gestione dello stato basata sul contesto HTTP

SignalR Le app non devono usare lo stato della sessione e altri approcci di gestione dello stato che si basano su un contesto HTTP stabile per archiviare le informazioni. SignalRle app possono archiviare lo stato per ogni connessione nell'hubContext.Items. Per altre informazioni e approcci alternativi per la gestione dello stato per Blazor Server le app, vedere ASP.NET Gestione dello stato coreBlazor.

Cookies

Cookies archivia i dati tra le richieste. Poiché cookievengono inviati con ogni richiesta, le dimensioni devono essere mantenute al minimo. Idealmente, solo un identificatore deve essere archiviato in un cookie oggetto con i dati archiviati dall'app. La maggior parte dei browser limita cookie le dimensioni a 4096 byte. Per ogni dominio sono disponibili solo un numero limitato di cookies.

Poiché cookiesono soggetti a manomissioni, devono essere convalidati dall'app. Cookies può essere eliminato dagli utenti e scadere nei client. Tuttavia, cookies è in genere la forma più durevole di persistenza dei dati nel client.

Cookies viene spesso usato per la personalizzazione, in cui il contenuto viene personalizzato per un utente noto. L'utente viene solo identificato e non autenticato nella maggior parte dei casi. cookie può archiviare il nome dell'utente, il nome dell'account o l'ID utente univoco, ad esempio un GUID. Può cookie essere usato per accedere alle impostazioni personalizzate dell'utente, ad esempio il colore di sfondo del sito Web preferito.

Vedere le normative generali sulla protezione dei dati dell'Unione europea (GDPR) quando si emettono cookiee gestiscono problemi di privacy. Per altre informazioni, vedere il supporto per il Regolamento generale sulla protezione dei dati in ASP.NET Core.

Stato della sessione

Lo stato della sessione è uno scenario di ASP.NET Core per l'archiviazione dei dati utente mentre l'utente visualizza un'app Web. Lo stato della sessione usa un archivio gestito dall'app per rendere persistenti i dati tra le richieste provenienti da un client. I dati della sessione sono supportati da una cache e considerati dati temporanei. Il sito deve continuare a funzionare senza i dati della sessione. I dati critici dell'applicazione devono essere archiviati nel database utente e memorizzati nella cache della sessione solo al fine di ottimizzare le prestazioni.

La sessione non è supportata nelle SignalR app perché un SignalR hub può essere eseguito indipendentemente da un contesto HTTP. Ad esempio, ciò può verificarsi quando una lunga richiesta di polling viene mantenuta aperta da un hub oltre la durata del contesto HTTP della richiesta.

ASP.NET Core mantiene lo stato della sessione fornendo un cookie al client che contiene un ID sessione. ID cookie sessione:

  • Viene inviato all'app con ogni richiesta.
  • Viene usato dall'app per recuperare i dati della sessione.

Lo stato della sessione presenta i comportamenti seguenti:

  • La sessione cookie è specifica del browser. Le sessioni non vengono condivise tra i browser.
  • Le sessioni cookievengono eliminate al termine della sessione del browser.
  • Se viene ricevuto un oggetto cookie per una sessione scaduta, viene creata una nuova sessione che usa la stessa sessione cookie.
  • Le sessioni vuote non vengono mantenute. La sessione deve avere almeno un valore impostato per rendere persistente la sessione tra le richieste. Se una sessione non viene conservata, viene generato un nuovo ID sessione per ogni nuova richiesta.
  • L'app conserva una sessione per un periodo di tempo limitato dopo l'ultima richiesta. L'app imposta il timeout della sessione o usa il valore predefinito, pari a 20 minuti. Lo stato della sessione è ideale per l'archiviazione dei dati utente:
    • Questo è specifico di una sessione specifica.
    • Dove i dati non richiedono l'archiviazione permanente tra le sessioni.
  • I dati della sessione vengono eliminati quando viene chiamata l'implementazione ISession.Clear o alla scadenza della sessione.
  • Non esiste alcun meccanismo predefinito per informare il codice dell'app che un browser client è stato chiuso o quando la sessione cookie viene eliminata o scaduta nel client.
  • Per impostazione predefinita, lo stato cookiedella sessione non è contrassegnato come essenziale. Lo stato della sessione non è funzionale a meno che il rilevamento non sia consentito dal visitatore del sito. Per altre informazioni, vedere il supporto per il Regolamento generale sulla protezione dei dati in ASP.NET Core.

Avviso

Evitare di archiviare dati sensibili nello stato della sessione. L'utente potrebbe non chiudere il browser e cancellare la sessione cookie. Alcuni browser mantengono la sessione cookievalida tra le finestre del browser. Una sessione potrebbe non essere limitata a un singolo utente. L'utente successivo potrebbe continuare a esplorare l'app con la stessa sessione cookie.

Il provider di cache in memoria archivia i dati della sessione nella memoria del server in cui si trova l'app. In uno scenario di server farm:

  • Usare sessioni permanenti per associare ogni sessione a un'istanza specifica dell'app in un singolo server. Il Servizio app di Azure usa il modulo Application Request Routing (ARR) per applicare le sessioni permanenti per impostazione predefinita. Tuttavia le sessioni permanenti possono compromettere la scalabilità e complicare gli aggiornamenti delle app Web. Un approccio migliore è usare la cache distribuita Redis o SQL Server, che non richiede sessioni permanenti. Per altre informazioni, vedere Memorizzazione nella cache distribuita in ASP.NET Core.
  • La sessione cookie viene crittografata tramite IDataProtector. La protezione dei dati deve essere configurata correttamente per leggere le sessioni cookiein ogni computer. Per altre informazioni, vedere ASP.NET Panoramica della protezione dei dati di base e provider di archiviazione delle chiavi.

Configurare lo stato della sessione

Pacchetto Microsoft.AspNetCore.Session :

  • Viene incluso in modo implicito dal framework.
  • Fornisce middleware per la gestione dello stato della sessione.

Per abilitare il middleware della sessione, Startup deve contenere:

Il codice seguente indica come configurare il provider della sessione in memoria con un'implementazione in memoria predefinita di 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();
        });
    }
}

Il codice precedente imposta un breve timeout per semplificare il test.

L'ordine del middleware è importante. Chiamare UseSession dopo UseRouting e prima UseEndpointsdi . Vedere Ordinamento del middleware.

HttpContext. Session è disponibile dopo che è stato configurato lo stato della sessione.

Non è possibile accedere a HttpContext.Session prima di chiamare UseSession.

Non è possibile creare una nuova sessione con una nuova sessione cookie dopo che l'app ha iniziato a scrivere nel flusso di risposta. L'eccezione viene registrata nel log del server Web e non è visualizzata nel browser.

Caricare lo stato della sessione in modo asincrono

Il provider di sessione predefinito in ASP.NET Core carica i record di sessione dall'archivio di backup sottostante IDistributedCache in modo asincrono solo se il ISession.LoadAsync metodo viene chiamato in modo esplicito prima dei TryGetValuemetodi , Seto Remove . Se LoadAsync non viene chiamato per primo, il record di sessione sottostante viene caricato in modo sincrono e ciò può compromettere le prestazioni su larga scala.

Per applicare questo modello alle app, eseguire il wrapping delle DistributedSessionStore implementazioni e DistributedSession con versioni che generano un'eccezione se il LoadAsync metodo non viene chiamato prima TryGetValuedi , Seto Remove. Registrare le versioni con wrapping nel contenitore di servizi.

Opzioni di sessione

Per eseguire l'override delle impostazioni predefinite della sessione, usare SessionOptions.

Opzione Descrizione
Cookie Determina le impostazioni utilizzate per creare l'oggetto cookie. Name il valore predefinito è SessionDefaults.CookieName (.AspNetCore.Session). Path il valore predefinito è SessionDefaults.CookiePath (/). SameSite il valore predefinito è SameSiteMode.Lax (1). Il valore predefinito di HttpOnly è true. Il valore predefinito di IsEssential è false.
IdleTimeout Il valore IdleTimeout indica quanto tempo la sessione può rimanere inattiva prima che il relativo contenuto venga abbandonato. Ogni accesso alla sessione reimposta il timeout. Questa impostazione si applica solo al contenuto della sessione, non a cookie. Il valore predefinito è 20 minuti.
IOTimeout L'intervallo di tempo massimo consentito per caricare una sessione dall'archivio o per eseguirne il commit nell'archivio. Questa impostazione può essere applicabile solo alle operazioni asincrone. Questo timeout può essere disabilitato tramite InfiniteTimeSpan. Il valore predefinito è 1 minuto.

La sessione usa un cookie oggetto per tenere traccia e identificare le richieste da un singolo browser. Per impostazione predefinita, questo cookie nome è .AspNetCore.Sessione usa un percorso di /. Poiché il cookie valore predefinito non specifica un dominio, non viene reso disponibile per lo script sul lato client nella pagina (perché HttpOnly per impostazione predefinita è true).

Per eseguire l'override delle impostazioni predefinite cookie della sessione, usare 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();
}

L'app usa la IdleTimeout proprietà per determinare per quanto tempo una sessione può essere inattiva prima che il relativo contenuto nella cache del server venga abbandonato. Questa proprietà è indipendente dalla cookie scadenza. Ogni richiesta che passa attraverso il middleware di sessione reimposta il timeout.

Lo stato della sessione è non di blocco. Se due richieste tentano simultaneamente di modificare il contenuto di una sessione, l'ultima richiesta sostituisce la prima. Session viene implementata come sessione coerente, ovvero tutto il contenuto viene archiviato insieme. Quando due richieste tentano di modificare diversi valori della sessione, l'ultima richiesta può sostituire le modifiche apportate alla sessione dalla prima.

Impostare e ottenere i valori della sessione

Lo stato della sessione è accessibile da una classe Pages PageModel o da una Razor classe MVC Controller con HttpContext.Session. Questa proprietà è un'implementazione ISession .

L'implementazione ISession offre diversi metodi di estensione per impostare e recuperare i valori interi e stringa. I metodi di estensione si trovano nello spazio dei Microsoft.AspNetCore.Http nomi .

Metodi di estensione ISession:

L'esempio seguente recupera il valore della sessione per la IndexModel.SessionKeyName chiave (_Name nell'app di esempio) in una Razor pagina Pages:

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

...

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

L'esempio seguente illustra come impostare e ottenere un intero e una stringa:

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

Tutti i dati della sessione devono essere serializzati per abilitare uno scenario di cache distribuita, anche quando si usa la cache in memoria. I serializzatori stringa e integer vengono forniti dai metodi di estensione di ISession. I tipi complessi devono essere serializzati dall'utente usando un altro meccanismo, ad esempio JSON.

Usare il codice di esempio seguente per serializzare gli oggetti:

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

L'esempio seguente illustra come impostare e ottenere un oggetto serializzabile con la SessionExtensions classe :

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

TempData

ASP.NET Core espone Razor Pages TempData o Controller TempData. Questa proprietà archivia i dati finché non viene letto in un'altra richiesta. I metodi Keep(String) e Peek(string) possono essere usati per esaminare i dati senza eliminazione alla fine della richiesta. Mantieni contrassegna tutti gli elementi nel dizionario per la conservazione. TempData è:

  • Utile per il reindirizzamento quando i dati sono necessari per più di una singola richiesta.
  • Implementato dai TempData provider che usano cookielo stato di sessione o s.

Esempi di TempData

Si consideri la pagina seguente che 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");
    }
}

Nella pagina seguente viene visualizzato TempData["Message"]:

@page
@model IndexModel

<h1>Peek Contacts</h1>

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

@*Content removed for brevity.*@

Nel markup precedente, alla fine della richiesta, TempData["Message"] non viene eliminato perché Peek viene usato. L'aggiornamento della pagina visualizza il contenuto di TempData["Message"].

Il markup seguente è simile al codice precedente, ma usa Keep per mantenere i dati alla fine della richiesta:

@page
@model IndexModel

<h1>Contacts Keep</h1>

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

@*Content removed for brevity.*@

Lo spostamento tra le pagine IndexPeek e IndexKeep non eliminerà TempData["Message"].

Il codice seguente visualizza TempData["Message"], ma alla fine della richiesta viene TempData["Message"] eliminato:

@page
@model IndexModel

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

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

@*Content removed for brevity.*@

Provider TempData

Il cookieprovider TempData basato su viene usato per impostazione predefinita per archiviare TempData in cookies.

I cookie dati vengono crittografati usando IDataProtector, codificati con Base64UrlTextEncoder, quindi in blocchi. La dimensione massima cookie è inferiore a 4096 byte a causa della crittografia e della suddivisione in blocchi. I cookie dati non vengono compressi perché la compressione dei dati crittografati può causare problemi di sicurezza, ad CRIME esempio gli attacchi e BREACH . Per altre informazioni sul cookieprovider TempData basato su , vedere CookieTempDataProvider.

Scegliere un provider TempData

La scelta di un provider TempData implica diverse considerazioni, tra cui:

  • L'app utilizza già lo stato sessione? In tal caso, l'uso del provider TempData dello stato della sessione non comporta costi aggiuntivi per l'app oltre le dimensioni dei dati.
  • L'app usa TempData solo con moderazione per quantità relativamente ridotte di dati, fino a 500 byte? In tal caso, il cookie provider TempData aggiunge un costo ridotto a ogni richiesta che contiene TempData. Se no, il provider TempData con stato sessione può essere utile per evitare sequenze di andata e ritorno di grandi quantità di dati in ogni richiesta fino a quando il contenuto TempData non viene consumato.
  • L'app viene eseguita in un server farm in più server? In tal caso, non è necessaria alcuna configurazione aggiuntiva per l'uso del cookie provider TempData all'esterno della protezione dei dati (vedere ASP.NET Panoramica della protezione dei dati di base e provider di archiviazione delle chiavi).

La maggior parte dei client Web, ad esempio i Web browser, applica limiti alle dimensioni massime di ognuno cookie e al numero totale di cookies. Quando si usa il cookie provider TempData, verificare che l'app non superi questi limiti. Considerare la dimensione totale dei dati. Tenere conto dell'aumento delle dimensioni a causa della crittografia e della suddivisione in cookie blocchi.

Configurare il provider TempData

Il cookieprovider TempData basato su è abilitato per impostazione predefinita.

Per abilitare il provider TempData basato su sessione, usare il AddSessionStateTempDataProvider metodo di estensione. È necessaria una sola chiamata 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();
    });
}

Stringhe di query

È possibile passare una quantità limitata di dati da una richiesta a un'altra aggiungendo i dati alla stringa di query della nuova richiesta. Questo è utile per l'acquisizione dello stato con una modalità persistente, che consente la condivisione dei collegamenti con stato incorporato tramite posta elettronica o social network. Poiché le stringhe di query dell'URL sono pubbliche, non usare mai le stringhe di query per i dati sensibili.

Oltre alla condivisione non intenzionale, inclusi i dati nelle stringhe di query, l'app può esporre l'app agli attacchi CSRF (Cross-Site Request Forgery). Qualsiasi stato della sessione mantenuto deve proteggere dagli attacchi CSRF. Per altre informazioni, vedere Prevenire attacchi tramite richieste intersito false (XSRF/CSRF) in ASP.NET Core.

Campi nascosti

I dati possono essere salvati in campi modulo nascosti e pubblicati di nuovo nella richiesta successiva. Questo si verifica spesso nei moduli a più pagine. Poiché il client potenzialmente può alterare i dati, l'app deve sempre ripetere la convalida dei dati archiviati nei campi nascosti.

HttpContext.Items

La HttpContext.Items raccolta viene utilizzata per archiviare i dati durante l'elaborazione di una singola richiesta. Il contenuto della raccolta viene rimosso al termine dell'elaborazione della richiesta. La raccolta Items spesso viene usata per consentire ai componenti o al middleware di comunicare quando operano in momenti diversi durante una richiesta e non è disponibile un metodo diretto per passare i parametri.

Nell'esempio seguente il middleware aggiunge isVerified alla Items raccolta:

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

Per il middleware usato solo in una singola app, le chiavi fisse string sono accettabili. Il middleware condiviso tra le app deve usare chiavi di oggetto univoche per evitare conflitti di chiavi. L'esempio seguente illustra come usare una chiave oggetto univoca definita in una classe 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>();
    }
}

Altri elementi di codice possono accedere al valore archiviato in HttpContext.Items usando la chiave esposta dalla classe middleware:

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

Questo approccio ha anche il vantaggio di eliminare l'uso di stringhe chiave nel codice.

Cache

La memorizzazione nella cache è un modo efficiente per archiviare e recuperare dati. L'app può controllare la durata degli elementi della cache. Per altre informazioni, vedere Memorizzazione nella cache delle risposte in ASP.NET Core.

I dati memorizzati nella cache non sono associati a una richiesta, un utente o una sessione specifici. Non memorizzare nella cache i dati specifici dell'utente che possono essere recuperati da altre richieste utente.

Per memorizzare nella cache i dati a livello di applicazione, vedere Cache in memoria in ASP.NET Core.

Errori comuni

  • "Unable to resolve service for type 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' while attempting to activate 'Microsoft.AspNetCore.Session.DistributedSessionStore'." (Risoluzione servizio non riuscita per il tipo 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' durante il tentativo di attivazione di 'Microsoft.AspNetCore.Session.DistributedSessionStore').

    Ciò è in genere causato dalla mancata configurazione di almeno un'implementazione IDistributedCache . Per altre informazioni, vedere Memorizzazione nella cache distribuita in ASP.NET Core e cache in memoria in ASP.NET Core.

Se il middleware della sessione non riesce a rendere persistente una sessione:

  • Il middleware registra l'eccezione e la richiesta continua normalmente.
  • Ciò provoca un comportamento imprevedibile.

Il middleware della sessione può non rendere persistente una sessione se l'archivio di backup non è disponibile. Ad esempio, un utente archivia un carrello acquisti nella sessione. L'utente aggiunge un articolo al carrello ma il commit ha esito negativo. L'app non riconosce l'errore e segnala all'utente che l'articolo è stato aggiunto al carrello anche se non è vero.

L'approccio consigliato per verificare la presenza di errori consiste nel chiamare await feature.Session.CommitAsync quando l'app ha completato la scrittura nella sessione. CommitAsync genera un'eccezione se l'archivio di backup non è disponibile. Se CommitAsync ha esito negativo, l'app è in grado di elaborare l'eccezione. LoadAsync genera le stesse condizioni quando l'archivio dati non è disponibile.

Risorse aggiuntive

Ospitare ASP.NET Core in una web farm