Zarządzanie sesjami i stanami na platformie ASP.NET Core

Autor : Rick Anderson, Kirk Larkin i Diana LaRose

HTTP to protokół bezstanowy. Domyślnie żądania HTTP są niezależnymi komunikatami, które nie zachowują wartości użytkowników. W tym artykule opisano kilka metod zachowywania danych użytkownika między żądaniami.

Zarządzanie stanem

Stan można przechowywać przy użyciu kilku metod. Każde podejście zostało opisane w dalszej części tego artykułu.

Podejście do magazynu Mechanizm magazynowania
Cookies HTTP cookies. Może zawierać dane przechowywane przy użyciu kodu aplikacji po stronie serwera.
Stan sesji Kod aplikacji po stronie serwera i protokołu HTTP cookie
TempData Stan sesji lub protokołu HTTP cookie
Ciągi zapytań Ciągi zapytań HTTP
Ukryte pola Pola formularza HTTP
HttpContext.Items Kod aplikacji po stronie serwera
Cache Kod aplikacji po stronie serwera

SignalR/Blazor Server i zarządzanie stanem opartym na kontekście HTTP

SignalR aplikacje nie powinny używać stanu sesji i innych metod zarządzania stanem, które opierają się na stabilnym kontekście HTTP do przechowywania informacji. SignalR aplikacje mogą przechowywać stan poszczególnych połączeń w Context.Items centrum. Aby uzyskać więcej informacji i alternatywnych metod zarządzania stanami dla Blazor Server aplikacji, zobacz ASP.NET Zarządzanie stanem podstawowymBlazor.

CookieS

Cookies przechowują dane między żądaniami. Ponieważ cookies są wysyłane z każdym żądaniem, ich rozmiar powinien być zachowany do minimum. W idealnym przypadku tylko identyfikator powinien być przechowywany w cookie obiekcie z danymi przechowywanymi przez aplikację. Większość przeglądarek ogranicza cookie rozmiar do 4096 bajtów. Dla każdej domeny jest dostępna tylko ograniczona liczba cookies.

Ze względu cookiena to, że elementy podlegają manipulacji, muszą być weryfikowane przez aplikację. CookieS można usunąć przez użytkowników i wygasać na klientach. Jednak cookies są na ogół najbardziej trwałą formą trwałości danych na kliencie.

Cookies są często używane do personalizacji, gdzie zawartość jest dostosowywana dla znanego użytkownika. Użytkownik jest identyfikowany tylko i nie jest uwierzytelniany w większości przypadków. Może cookie przechowywać nazwę użytkownika, nazwę konta lub unikatowy identyfikator użytkownika, taki jak identyfikator GUID. Może cookie służyć do uzyskiwania dostępu do spersonalizowanych ustawień użytkownika, takich jak preferowany kolor tła witryny internetowej.

Zobacz Ogólne przepisy o ochronie danych (RODO) Unii Europejskiej podczas wydawania cookiei radzenia sobie z problemami dotyczącymi prywatności. Aby uzyskać więcej informacji, zobacz Ogólne wsparcie dotyczące rozporządzenia o ochronie danych (RODO) w ASP.NET Core.

Stan sesji

Stan sesji to ASP.NET Core scenariusz przechowywania danych użytkownika podczas przeglądania aplikacji internetowej przez użytkownika. Stan sesji używa magazynu obsługiwanego przez aplikację do utrwalania danych między żądaniami od klienta. Dane sesji są wspierane przez pamięć podręczną i uważane za dane efemeryczne. Lokacja powinna nadal działać bez danych sesji. Krytyczne dane aplikacji powinny być przechowywane w bazie danych użytkownika i buforowane w sesji tylko jako optymalizacja wydajności.

Sesja nie jest obsługiwana w SignalR aplikacjach, ponieważ SignalR centrum może być wykonywane niezależnie od kontekstu HTTP. Na przykład może to wystąpić, gdy długie żądanie sondowania jest otwarte przez centrum poza okresem istnienia kontekstu HTTP żądania.

ASP.NET Core utrzymuje stan sesji, podając klientowi, cookie który zawiera identyfikator sesji. Identyfikator cookie sesji:

  • Jest wysyłany do aplikacji z każdym żądaniem.
  • Jest używany przez aplikację do pobierania danych sesji.

Stan sesji wykazuje następujące zachowania:

  • Sesja cookie jest specyficzna dla przeglądarki. Sesje nie są udostępniane w przeglądarkach.
  • Sesje cookiesą usuwane po zakończeniu sesji przeglądarki.
  • cookie Jeśli element zostanie odebrany dla wygasłej sesji, zostanie utworzona nowa sesja, która używa tej samej sesji cookie.
  • Puste sesje nie są zachowywane. Sesja musi mieć co najmniej jedną wartość ustawioną, aby utrwała sesję między żądaniami. Gdy sesja nie zostanie zachowana, dla każdego nowego żądania zostanie wygenerowany nowy identyfikator sesji.
  • Aplikacja zachowuje sesję przez ograniczony czas po ostatnim żądaniu. Aplikacja ustawia limit czasu sesji lub używa wartości domyślnej 20 minut. Stan sesji jest idealny do przechowywania danych użytkownika:
    • Jest to specyficzne dla określonej sesji.
    • Gdzie dane nie wymagają trwałego przechowywania między sesjami.
  • Dane sesji są usuwane, gdy implementacja ISession.Clear jest wywoływana lub gdy sesja wygaśnie.
  • Nie ma domyślnego mechanizmu informowania o kodzie aplikacji, że przeglądarka klienta została zamknięta lub gdy sesja cookie zostanie usunięta lub wygasła na kliencie.
  • Stany cookiesesji nie są domyślnie oznaczone jako istotne. Stan sesji nie jest funkcjonalny, chyba że śledzenie jest dozwolone przez odwiedzających witrynę. Aby uzyskać więcej informacji, zobacz Ogólne wsparcie dotyczące rozporządzenia o ochronie danych (RODO) w ASP.NET Core.
  • Uwaga: nie ma zamiany cookiefunkcji mniej sesji z platformy ASP.NET Framework, ponieważ jest uważana za niepewną i może prowadzić do ataków naprawy sesji.

Ostrzeżenie

Nie przechowuj poufnych danych w stanie sesji. Użytkownik może nie zamknąć przeglądarki i wyczyścić sesję cookie. Niektóre przeglądarki utrzymują prawidłowe sesje cookiew oknach przeglądarki. Sesja może nie być ograniczona do jednego użytkownika. Następny użytkownik może nadal przeglądać aplikację przy użyciu tej samej sesji cookie.

Dostawca pamięci podręcznej w pamięci przechowuje dane sesji w pamięci serwera, na którym znajduje się aplikacja. W scenariuszu farmy serwerów:

Konfigurowanie stanu sesji

Oprogramowanie pośredniczące do zarządzania stanem sesji jest zawarte w strukturze. Aby włączyć oprogramowanie pośredniczące sesji, Program.cs musi zawierać:

Poniższy kod pokazuje, jak skonfigurować dostawcę sesji w pamięci z domyślną implementacją w pamięci :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();

Powyższy kod ustawia krótki limit czasu, aby uprościć testowanie.

Kolejność oprogramowania pośredniczącego jest ważna. Wywołaj połączenie UseSession po UseRouting i przed MapRazorPages i MapDefaultControllerRoute . Zobacz Porządkowanie oprogramowania pośredniczącego.

HttpContext.Session jest dostępny po skonfigurowaniu stanu sesji.

HttpContext.Session Nie można uzyskać dostępu przed UseSession wywołaniem.

Nie można utworzyć nowej sesji z nową sesją cookie po rozpoczęciu zapisywania aplikacji w strumieniu odpowiedzi. Wyjątek jest rejestrowany w dzienniku serwera internetowego i nie jest wyświetlany w przeglądarce.

Załaduj stan sesji asynchronicznie

Domyślny dostawca sesji w ASP.NET Core ładuje rekordy sesji z bazowego IDistributedCache magazynu zapasowego asynchronicznie tylko wtedy, gdy ISession.LoadAsync metoda jest jawnie wywoływana przed metodami TryGetValue, Setlub Remove . Jeśli LoadAsync nie jest wywoływany jako pierwszy, podstawowy rekord sesji jest ładowany synchronicznie, co może spowodować karę za wydajność na dużą skalę.

Aby aplikacje wymuszały ten wzorzec, opakuj implementacje i DistributedSession wersjami, które zgłaszają wyjątek, jeśli LoadAsync metoda nie jest wywoływana przed TryGetValueSet, lub Remove.DistributedSessionStore Zarejestruj opakowane wersje w kontenerze usług.

Opcje sesji

Aby zastąpić wartości domyślne sesji, użyj polecenia SessionOptions.

Opcja Opis
Cookie Określa ustawienia używane do utworzenia elementu cookie. Name wartość domyślna to SessionDefaults.CookieName (.AspNetCore.Session). Path wartość domyślna to SessionDefaults.CookiePath (/). SameSite wartość domyślna to SameSiteMode.Lax (1). Właściwość HttpOnly domyślnie przyjmuje wartość true. Właściwość IsEssential domyślnie przyjmuje wartość false.
IdleTimeout Wskazuje IdleTimeout , jak długo sesja może być bezczynna, zanim jej zawartość zostanie porzucona. Każdy dostęp do sesji resetuje limit czasu. To ustawienie dotyczy tylko zawartości sesji, a nie .cookie Wartość domyślna to 20 minut.
IOTimeout Maksymalny czas ładowania sesji ze sklepu lub zatwierdzenia go z powrotem do magazynu. To ustawienie może dotyczyć tylko operacji asynchronicznych. Ten limit czasu można wyłączyć przy użyciu polecenia InfiniteTimeSpan. Wartość domyślna to 1 minuta.

Sesja używa elementu cookie do śledzenia i identyfikowania żądań z jednej przeglądarki. Domyślnie jest to cookie nazwa .AspNetCore.Sessioni używa ścieżki /. Ponieważ wartość domyślna cookie nie określa domeny, nie jest ona udostępniana skryptowi po stronie klienta na stronie (ponieważ HttpOnly wartość domyślna to true).

Aby zastąpić cookie wartości domyślne sesji, użyj polecenia 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();

Aplikacja używa IdleTimeout właściwości , aby określić, jak długo sesja może być bezczynna, zanim jej zawartość w pamięci podręcznej serwera zostanie porzucona. Ta właściwość jest niezależna cookie od wygaśnięcia. Każde żądanie przekazywane przez oprogramowanie pośredniczące sesji resetuje limit czasu.

Stan sesji jest nieblokujący. Jeśli dwa żądania jednocześnie próbują zmodyfikować zawartość sesji, ostatnie żądanie zastępuje pierwsze. Session program jest implementowany jako spójna sesja, co oznacza, że cała zawartość jest przechowywana razem. Gdy dwa żądania próbują zmodyfikować różne wartości sesji, ostatnie żądanie może zastąpić zmiany sesji wprowadzone przez pierwszy.

Ustawianie i pobieranie wartości sesji

Dostęp do stanu sesji jest uzyskiwany z Razor klasy Pages PageModel lub klasy MVC Controller za pomocą polecenia HttpContext.Session. Ta właściwość jest implementacją ISession .

Implementacja ISession udostępnia kilka metod rozszerzenia do ustawiania i pobierania wartości całkowitych i ciągów. Metody rozszerzenia znajdują się w Microsoft.AspNetCore.Http przestrzeni nazw.

ISession metody rozszerzeń:

Poniższy przykład pobiera wartość sesji dla IndexModel.SessionKeyName klucza (_Name w przykładowej Razor aplikacji) na stronie Strony:

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

...

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

W poniższym przykładzie pokazano, jak ustawić i pobrać liczbę całkowitą oraz ciąg:

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

Na stronie są wyświetlane następujące znaczniki Razor sesji:

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


Wszystkie dane sesji muszą być serializowane, aby umożliwić scenariusz rozproszonej pamięci podręcznej, nawet w przypadku korzystania z pamięci podręcznej. Serializatory ciągów i liczb całkowitych są dostarczane przez metody ISessionrozszerzenia . Typy złożone muszą być serializowane przez użytkownika przy użyciu innego mechanizmu, takiego jak JSWŁ.

Użyj następującego przykładowego kodu, aby serializować obiekty:

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

W poniższym przykładzie pokazano, jak ustawić i pobrać obiekt możliwy do serializacji z klasą 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));

        }
    }
}

Ostrzeżenie

Przechowywanie obiektu na żywo w sesji powinno być używane z ostrożnością, ponieważ istnieje wiele problemów, które mogą wystąpić z serializowanymi obiektami. Aby uzyskać więcej informacji, zobacz Sesje powinny być dozwolone do przechowywania obiektów (dotnet/aspnetcore #18159).

TempData

ASP.NET Core uwidacznia strony TempData lub Controller TempData.Razor Ta właściwość przechowuje dane, dopóki nie zostanie odczytany w innym żądaniu. Metody Keep(String) i Peek(string) mogą służyć do badania danych bez usuwania na końcu żądania. Zachowaj znaczniki wszystkich elementów w słowniku na potrzeby przechowywania. TempData Jest:

  • Przydatne w przypadku przekierowania, gdy dane są wymagane dla więcej niż jednego żądania.
  • Zaimplementowane przez TempData dostawców przy użyciu cookiestanu s lub sesji.

Przykłady danych tempData

Rozważmy następującą stronę, która tworzy klienta:

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

Na poniższej stronie zostanie wyświetlona następująca TempData["Message"]strona:

@page
@model IndexModel

<h1>Peek Contacts</h1>

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

@*Content removed for brevity.*@

W poprzednim znaczniku na końcu żądania nie jest usuwana, TempData["Message"] ponieważ Peek jest używana. Odświeżenie strony powoduje wyświetlenie zawartości elementu TempData["Message"].

Poniższy znacznik jest podobny do poprzedniego kodu, ale używa go Keep do zachowania danych na końcu żądania:

@page
@model IndexModel

<h1>Contacts Keep</h1>

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

@*Content removed for brevity.*@

Nawigowanie między stronami IndexPeek i IndexKeep nie spowoduje usunięcia TempData["Message"]elementu .

Poniższy kod wyświetla TempData["Message"]ciąg , ale na końcu żądania TempData["Message"] jest usuwany:

@page
@model IndexModel

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

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

@*Content removed for brevity.*@

Dostawcy tempData

Dostawca cookietempData oparty na protokole TempData jest domyślnie używany do przechowywania danych TempData w programie cookies.

Dane cookie są szyfrowane przy użyciu metody , zakodowanej za pomocą IDataProtectorBase64UrlTextEncodermetody , a następnie fragmentowane. Maksymalny cookie rozmiar jest mniejszy niż 4096 bajtów ze względu na szyfrowanie i fragmentowanie. Dane cookie nie są kompresowane, ponieważ kompresowanie zaszyfrowanych danych może prowadzić do problemów z zabezpieczeniami, takich jak CRIME ataki i BREACH . Aby uzyskać więcej informacji na temat opartego na cookiedostawcy TempData, zobacz CookieTempDataProvider.

Wybieranie dostawcy tempData

Wybranie dostawcy TempData obejmuje kilka zagadnień, takich jak:

  • Czy aplikacja używa już stanu sesji? Jeśli tak, użycie dostawcy TempData stanu sesji nie ma dodatkowych kosztów dla aplikacji poza rozmiarem danych.
  • Czy aplikacja używa tempData tylko stosunkowo małych ilości danych, do 500 bajtów? Jeśli tak, cookie dostawca TempData dodaje niewielki koszt do każdego żądania, które niesie ze sobą tempData. Jeśli nie, dostawca TempData stanu sesji może być korzystny, aby uniknąć zaokrąglania dużej ilości danych w każdym żądaniu, dopóki dane tempData nie zostaną zużyte.
  • Czy aplikacja działa w farmie serwerów na wielu serwerach? Jeśli tak, nie ma dodatkowej konfiguracji wymaganej cookie do korzystania z dostawcy TempData poza ochroną danych. Aby uzyskać więcej informacji, zobacz ASP.NET Core Data Protection Overview and Key Storage providers (Omówienie podstawowej ochrony danych i dostawcy magazynu kluczy).

Większość klientów internetowych, takich jak przeglądarki internetowe, wymusza limity maksymalnego rozmiaru każdego cookie i łączną liczbę cookies. W przypadku korzystania z dostawcy cookie TempData sprawdź, czy aplikacja nie przekroczy tych limitów. Rozważ łączny rozmiar danych. Uwzględnij wzrost rozmiaru cookie ze względu na szyfrowanie i fragmentowanie.

Konfigurowanie dostawcy TempData

Dostawca cookieTempData oparty na protokole TempData jest domyślnie włączony.

Aby włączyć dostawcę TempData opartego na sesji, użyj AddSessionStateTempDataProvider metody rozszerzenia. Wymagane jest tylko jedno wywołanie 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();

Ciągi zapytań

Ograniczoną ilość danych można przekazać z jednego żądania do innego, dodając je do ciągu zapytania nowego żądania. Jest to przydatne w przypadku przechwytywania stanu w trwały sposób, który umożliwia udostępnianie linków ze stanem osadzonym za pośrednictwem poczty e-mail lub sieci społecznościowych. Ponieważ ciągi zapytania adresu URL są publiczne, nigdy nie używaj ciągów zapytań dla poufnych danych.

Oprócz niezamierzonego udostępniania, w tym danych w ciągach zapytań, można uwidocznić aplikację na ataki między witrynami w celu fałszerowania (CSRF ). Każdy zachowany stan sesji musi chronić przed atakami CSRF. Aby uzyskać więcej informacji, zobacz Zapobieganie atakom z fałszowaniem żądań międzywitrynowych (XSRF/CSRF) na platformie ASP.NET Core.

Ukryte pola

Dane można zapisywać w ukrytych polach formularza i publikować je ponownie w następnym żądaniu. Jest to często spotykane w formularzach wielostronicowych. Ponieważ klient może potencjalnie manipulować danymi, aplikacja musi zawsze ponownie modyfikować dane przechowywane w ukrytych polach.

HttpContext.Items

Kolekcja HttpContext.Items służy do przechowywania danych podczas przetwarzania pojedynczego żądania. Zawartość kolekcji jest odrzucana po przetworzeniu żądania. Kolekcja Items jest często używana do zezwalania składnikom lub oprogramowaniem pośredniczącemu na komunikowanie się, gdy działają w różnych punktach w czasie podczas żądania i nie mają bezpośredniego sposobu przekazywania parametrów.

W poniższym przykładzie oprogramowanie pośredniczące dodaje isVerified do kolekcji 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();

W przypadku oprogramowania pośredniczącego, które jest używane tylko w jednej aplikacji, jest mało prawdopodobne, że użycie stałego string klucza spowoduje kolizję klucza. Jednak aby uniknąć możliwości całkowitej kolizji klucza, object można go użyć jako klucza elementu. Takie podejście jest szczególnie przydatne w przypadku oprogramowania pośredniczącego współużytkowanego między aplikacjami, a także zaletą wyeliminowania używania ciągów kluczy w kodzie. W poniższym przykładzie pokazano, jak używać klucza zdefiniowanego object w klasie oprogramowania pośredniczącego:

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

Inny kod może uzyskać dostęp do wartości przechowywanej przy HttpContext.Items użyciu klucza uwidocznionego przez klasę oprogramowania pośredniczącego:

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

Buforowanie to wydajny sposób przechowywania i pobierania danych. Aplikacja może kontrolować okres istnienia buforowanych elementów. Aby uzyskać więcej informacji, zobacz Buforowanie odpowiedzi w programie ASP.NET Core.

Dane buforowane nie są skojarzone z określonym żądaniem, użytkownikiem ani sesją. Nie buforuj danych specyficznych dla użytkownika, które mogą być pobierane przez inne żądania użytkownika.

Aby buforować dane w całej aplikacji, zobacz Buforowanie w pamięci w ASP.NET Core.

Sprawdzanie stanu sesji

ISession.IsAvailable ma na celu sprawdzenie błędów przejściowych. Wywołanie IsAvailable przed uruchomieniem oprogramowania pośredniczącego sesji zgłasza błąd InvalidOperationException.

Biblioteki, które muszą przetestować dostępność sesji, mogą używać polecenia HttpContext.Features.Get<ISessionFeature>()?.Session != null.

Typowe błędy

  • "Nie można rozpoznać usługi dla typu "Microsoft.Extensions". Buforowanie. Distributed.IDistributedCache" podczas próby aktywowania "Microsoft.AspNetCore.Session.DistributedSessionStore".

    Jest to zwykle spowodowane niepowodzeniem konfigurowania co najmniej jednej IDistributedCache implementacji. Aby uzyskać więcej informacji, zobacz Buforowanie rozproszone w ASP.NET Core i Pamięci podręcznej w pamięci w ASP.NET Core.

Jeśli oprogramowanie pośredniczące sesji nie będzie utrwalać sesji:

  • Oprogramowanie pośredniczące rejestruje wyjątek, a żądanie jest kontynuowane normalnie.
  • Prowadzi to do nieprzewidywalnego zachowania.

Oprogramowanie pośredniczące sesji może zakończyć się niepowodzeniem, jeśli magazyn kopii zapasowych nie jest dostępny. Na przykład użytkownik przechowuje koszyk w sesji. Użytkownik dodaje element do koszyka, ale zatwierdzenie kończy się niepowodzeniem. Aplikacja nie wie o awarii, dlatego zgłasza użytkownikowi, że element został dodany do koszyka, co nie jest prawdą.

Zalecaną metodą sprawdzania błędów jest wywołanie await feature.Session.CommitAsync podczas zapisywania aplikacji w sesji. CommitAsync zgłasza wyjątek, jeśli magazyn zapasowy jest niedostępny. W przypadku CommitAsync niepowodzenia aplikacja może przetworzyć wyjątek. LoadAsync zgłasza te same warunki, gdy magazyn danych jest niedostępny.

Dodatkowe zasoby

Wyświetl lub pobierz przykładowy kod (jak pobrać)

Hostowanie aplikacji ASP.NET Core na farmie internetowej

Autor : Rick Anderson, Kirk Larkin i Diana LaRose

HTTP to protokół bezstanowy. Domyślnie żądania HTTP są niezależnymi komunikatami, które nie zachowują wartości użytkowników. W tym artykule opisano kilka metod zachowywania danych użytkownika między żądaniami.

Wyświetl lub pobierz przykładowy kod (jak pobrać)

Zarządzanie stanem

Stan można przechowywać przy użyciu kilku metod. Każde podejście zostało opisane w dalszej części tego artykułu.

Podejście do magazynu Mechanizm magazynowania
Cookies HTTP cookies. Może zawierać dane przechowywane przy użyciu kodu aplikacji po stronie serwera.
Stan sesji Kod aplikacji po stronie serwera i protokołu HTTP cookie
TempData Stan sesji lub protokołu HTTP cookie
Ciągi zapytań Ciągi zapytań HTTP
Ukryte pola Pola formularza HTTP
HttpContext.Items Kod aplikacji po stronie serwera
Cache Kod aplikacji po stronie serwera

SignalR/Blazor Server i zarządzanie stanem opartym na kontekście HTTP

SignalR aplikacje nie powinny używać stanu sesji i innych metod zarządzania stanem, które opierają się na stabilnym kontekście HTTP do przechowywania informacji. SignalR aplikacje mogą przechowywać stan poszczególnych połączeń w Context.Items centrum. Aby uzyskać więcej informacji i alternatywnych metod zarządzania stanami dla Blazor Server aplikacji, zobacz ASP.NET Zarządzanie stanem podstawowymBlazor.

CookieS

Cookies przechowują dane między żądaniami. Ponieważ cookies są wysyłane z każdym żądaniem, ich rozmiar powinien być zachowany do minimum. W idealnym przypadku tylko identyfikator powinien być przechowywany w cookie obiekcie z danymi przechowywanymi przez aplikację. Większość przeglądarek ogranicza cookie rozmiar do 4096 bajtów. Dla każdej domeny jest dostępna tylko ograniczona liczba cookies.

Ze względu cookiena to, że elementy podlegają manipulacji, muszą być weryfikowane przez aplikację. CookieS można usunąć przez użytkowników i wygasać na klientach. Jednak cookies są na ogół najbardziej trwałą formą trwałości danych na kliencie.

Cookies są często używane do personalizacji, gdzie zawartość jest dostosowywana dla znanego użytkownika. Użytkownik jest identyfikowany tylko i nie jest uwierzytelniany w większości przypadków. Może cookie przechowywać nazwę użytkownika, nazwę konta lub unikatowy identyfikator użytkownika, taki jak identyfikator GUID. Może cookie służyć do uzyskiwania dostępu do spersonalizowanych ustawień użytkownika, takich jak preferowany kolor tła witryny internetowej.

Zobacz Ogólne przepisy o ochronie danych (RODO) Unii Europejskiej podczas wydawania cookiei radzenia sobie z problemami dotyczącymi prywatności. Aby uzyskać więcej informacji, zobacz Ogólne wsparcie dotyczące rozporządzenia o ochronie danych (RODO) w ASP.NET Core.

Stan sesji

Stan sesji to ASP.NET Core scenariusz przechowywania danych użytkownika podczas przeglądania aplikacji internetowej przez użytkownika. Stan sesji używa magazynu obsługiwanego przez aplikację do utrwalania danych między żądaniami od klienta. Dane sesji są wspierane przez pamięć podręczną i uważane za dane efemeryczne. Lokacja powinna nadal działać bez danych sesji. Krytyczne dane aplikacji powinny być przechowywane w bazie danych użytkownika i buforowane w sesji tylko jako optymalizacja wydajności.

Sesja nie jest obsługiwana w SignalR aplikacjach, ponieważ SignalR centrum może być wykonywane niezależnie od kontekstu HTTP. Na przykład może to wystąpić, gdy długie żądanie sondowania jest otwarte przez centrum poza okresem istnienia kontekstu HTTP żądania.

ASP.NET Core utrzymuje stan sesji, podając klientowi, cookie który zawiera identyfikator sesji. Identyfikator cookie sesji:

  • Jest wysyłany do aplikacji z każdym żądaniem.
  • Jest używany przez aplikację do pobierania danych sesji.

Stan sesji wykazuje następujące zachowania:

  • Sesja cookie jest specyficzna dla przeglądarki. Sesje nie są udostępniane w przeglądarkach.
  • Sesje cookiesą usuwane po zakończeniu sesji przeglądarki.
  • cookie Jeśli element zostanie odebrany dla wygasłej sesji, zostanie utworzona nowa sesja, która używa tej samej sesji cookie.
  • Puste sesje nie są zachowywane. Sesja musi mieć co najmniej jedną wartość ustawioną, aby utrwała sesję między żądaniami. Gdy sesja nie zostanie zachowana, dla każdego nowego żądania zostanie wygenerowany nowy identyfikator sesji.
  • Aplikacja zachowuje sesję przez ograniczony czas po ostatnim żądaniu. Aplikacja ustawia limit czasu sesji lub używa wartości domyślnej 20 minut. Stan sesji jest idealny do przechowywania danych użytkownika:
    • Jest to specyficzne dla określonej sesji.
    • Gdzie dane nie wymagają trwałego przechowywania między sesjami.
  • Dane sesji są usuwane, gdy implementacja ISession.Clear jest wywoływana lub gdy sesja wygaśnie.
  • Nie ma domyślnego mechanizmu informowania o kodzie aplikacji, że przeglądarka klienta została zamknięta lub gdy sesja cookie zostanie usunięta lub wygasła na kliencie.
  • Stany cookiesesji nie są domyślnie oznaczone jako istotne. Stan sesji nie jest funkcjonalny, chyba że śledzenie jest dozwolone przez odwiedzających witrynę. Aby uzyskać więcej informacji, zobacz Ogólne wsparcie dotyczące rozporządzenia o ochronie danych (RODO) w ASP.NET Core.

Ostrzeżenie

Nie przechowuj poufnych danych w stanie sesji. Użytkownik może nie zamknąć przeglądarki i wyczyścić sesję cookie. Niektóre przeglądarki utrzymują prawidłowe sesje cookiew oknach przeglądarki. Sesja może nie być ograniczona do jednego użytkownika. Następny użytkownik może nadal przeglądać aplikację przy użyciu tej samej sesji cookie.

Dostawca pamięci podręcznej w pamięci przechowuje dane sesji w pamięci serwera, na którym znajduje się aplikacja. W scenariuszu farmy serwerów:

Konfigurowanie stanu sesji

Pakiet Microsoft.AspNetCore.Session:

  • Jest dołączana niejawnie przez platformę.
  • Udostępnia oprogramowanie pośredniczące do zarządzania stanem sesji.

Aby włączyć oprogramowanie pośredniczące sesji, Startup musi zawierać:

Poniższy kod pokazuje, jak skonfigurować dostawcę sesji w pamięci z domyślną implementacją w pamięci :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();
        });
    }
}

Powyższy kod ustawia krótki limit czasu, aby uprościć testowanie.

Kolejność oprogramowania pośredniczącego jest ważna. Wywołaj połączenie UseSession po UseRouting i przed UseEndpoints. Zobacz Porządkowanie oprogramowania pośredniczącego.

HttpContext.Session jest dostępny po skonfigurowaniu stanu sesji.

HttpContext.Session Nie można uzyskać dostępu przed UseSession wywołaniem.

Nie można utworzyć nowej sesji z nową sesją cookie po rozpoczęciu zapisywania aplikacji w strumieniu odpowiedzi. Wyjątek jest rejestrowany w dzienniku serwera internetowego i nie jest wyświetlany w przeglądarce.

Załaduj stan sesji asynchronicznie

Domyślny dostawca sesji w ASP.NET Core ładuje rekordy sesji z bazowego IDistributedCache magazynu zapasowego asynchronicznie tylko wtedy, gdy ISession.LoadAsync metoda jest jawnie wywoływana przed metodami TryGetValue, Setlub Remove . Jeśli LoadAsync nie jest wywoływany jako pierwszy, podstawowy rekord sesji jest ładowany synchronicznie, co może spowodować karę za wydajność na dużą skalę.

Aby aplikacje wymuszały ten wzorzec, opakuj implementacje i DistributedSession wersjami, które zgłaszają wyjątek, jeśli LoadAsync metoda nie jest wywoływana przed TryGetValueSet, lub Remove.DistributedSessionStore Zarejestruj opakowane wersje w kontenerze usług.

Opcje sesji

Aby zastąpić wartości domyślne sesji, użyj polecenia SessionOptions.

Opcja Opis
Cookie Określa ustawienia używane do utworzenia elementu cookie. Name wartość domyślna to SessionDefaults.CookieName (.AspNetCore.Session). Path wartość domyślna to SessionDefaults.CookiePath (/). SameSite wartość domyślna to SameSiteMode.Lax (1). Właściwość HttpOnly domyślnie przyjmuje wartość true. Właściwość IsEssential domyślnie przyjmuje wartość false.
IdleTimeout Wskazuje IdleTimeout , jak długo sesja może być bezczynna, zanim jej zawartość zostanie porzucona. Każdy dostęp do sesji resetuje limit czasu. To ustawienie dotyczy tylko zawartości sesji, a nie .cookie Wartość domyślna to 20 minut.
IOTimeout Maksymalny czas ładowania sesji ze sklepu lub zatwierdzenia go z powrotem do magazynu. To ustawienie może dotyczyć tylko operacji asynchronicznych. Ten limit czasu można wyłączyć przy użyciu polecenia InfiniteTimeSpan. Wartość domyślna to 1 minuta.

Sesja używa elementu cookie do śledzenia i identyfikowania żądań z jednej przeglądarki. Domyślnie jest to cookie nazwa .AspNetCore.Sessioni używa ścieżki /. Ponieważ wartość domyślna cookie nie określa domeny, nie jest ona udostępniana skryptowi po stronie klienta na stronie (ponieważ HttpOnly wartość domyślna to true).

Aby zastąpić cookie wartości domyślne sesji, użyj polecenia 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();
}

Aplikacja używa IdleTimeout właściwości , aby określić, jak długo sesja może być bezczynna, zanim jej zawartość w pamięci podręcznej serwera zostanie porzucona. Ta właściwość jest niezależna cookie od wygaśnięcia. Każde żądanie przekazywane przez oprogramowanie pośredniczące sesji resetuje limit czasu.

Stan sesji jest nieblokujący. Jeśli dwa żądania jednocześnie próbują zmodyfikować zawartość sesji, ostatnie żądanie zastępuje pierwsze. Session program jest implementowany jako spójna sesja, co oznacza, że cała zawartość jest przechowywana razem. Gdy dwa żądania próbują zmodyfikować różne wartości sesji, ostatnie żądanie może zastąpić zmiany sesji wprowadzone przez pierwszy.

Ustawianie i pobieranie wartości sesji

Dostęp do stanu sesji jest uzyskiwany z Razor klasy Pages PageModel lub klasy MVC Controller za pomocą polecenia HttpContext.Session. Ta właściwość jest implementacją ISession .

Implementacja ISession udostępnia kilka metod rozszerzenia do ustawiania i pobierania wartości całkowitych i ciągów. Metody rozszerzenia znajdują się w Microsoft.AspNetCore.Http przestrzeni nazw.

ISession metody rozszerzeń:

Poniższy przykład pobiera wartość sesji dla IndexModel.SessionKeyName klucza (_Name w przykładowej Razor aplikacji) na stronie Strony:

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

...

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

W poniższym przykładzie pokazano, jak ustawić i pobrać liczbę całkowitą oraz ciąg:

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

Wszystkie dane sesji muszą być serializowane, aby umożliwić scenariusz rozproszonej pamięci podręcznej, nawet w przypadku korzystania z pamięci podręcznej. Serializatory ciągów i liczb całkowitych są dostarczane przez metody ISessionrozszerzenia . Typy złożone muszą być serializowane przez użytkownika przy użyciu innego mechanizmu, takiego jak JSWŁ.

Użyj następującego przykładowego kodu, aby serializować obiekty:

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

W poniższym przykładzie pokazano, jak ustawić i pobrać obiekt możliwy do serializacji z klasą SessionExtensions :

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

TempData

ASP.NET Core uwidacznia strony TempData lub Controller TempData.Razor Ta właściwość przechowuje dane, dopóki nie zostanie odczytany w innym żądaniu. Metody Keep(String) i Peek(string) mogą służyć do badania danych bez usuwania na końcu żądania. Zachowaj znaczniki wszystkich elementów w słowniku na potrzeby przechowywania. TempData Jest:

  • Przydatne w przypadku przekierowania, gdy dane są wymagane dla więcej niż jednego żądania.
  • Zaimplementowane przez TempData dostawców przy użyciu cookiestanu s lub sesji.

Przykłady danych tempData

Rozważmy następującą stronę, która tworzy klienta:

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

Na poniższej stronie zostanie wyświetlona następująca TempData["Message"]strona:

@page
@model IndexModel

<h1>Peek Contacts</h1>

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

@*Content removed for brevity.*@

W poprzednim znaczniku na końcu żądania nie jest usuwana, TempData["Message"] ponieważ Peek jest używana. Odświeżenie strony powoduje wyświetlenie zawartości elementu TempData["Message"].

Poniższy znacznik jest podobny do poprzedniego kodu, ale używa go Keep do zachowania danych na końcu żądania:

@page
@model IndexModel

<h1>Contacts Keep</h1>

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

@*Content removed for brevity.*@

Nawigowanie między stronami IndexPeek i IndexKeep nie spowoduje usunięcia TempData["Message"]elementu .

Poniższy kod wyświetla TempData["Message"]ciąg , ale na końcu żądania TempData["Message"] jest usuwany:

@page
@model IndexModel

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

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

@*Content removed for brevity.*@

Dostawcy tempData

Dostawca cookietempData oparty na protokole TempData jest domyślnie używany do przechowywania danych TempData w programie cookies.

Dane cookie są szyfrowane przy użyciu metody , zakodowanej za pomocą IDataProtectorBase64UrlTextEncodermetody , a następnie fragmentowane. Maksymalny cookie rozmiar jest mniejszy niż 4096 bajtów ze względu na szyfrowanie i fragmentowanie. Dane cookie nie są kompresowane, ponieważ kompresowanie zaszyfrowanych danych może prowadzić do problemów z zabezpieczeniami, takich jak CRIME ataki i BREACH . Aby uzyskać więcej informacji na temat opartego na cookiedostawcy TempData, zobacz CookieTempDataProvider.

Wybieranie dostawcy tempData

Wybranie dostawcy TempData obejmuje kilka zagadnień, takich jak:

  • Czy aplikacja używa już stanu sesji? Jeśli tak, użycie dostawcy TempData stanu sesji nie ma dodatkowych kosztów dla aplikacji poza rozmiarem danych.
  • Czy aplikacja używa tempData tylko stosunkowo małych ilości danych, do 500 bajtów? Jeśli tak, cookie dostawca TempData dodaje niewielki koszt do każdego żądania, które niesie ze sobą tempData. Jeśli nie, dostawca TempData stanu sesji może być korzystny, aby uniknąć zaokrąglania dużej ilości danych w każdym żądaniu, dopóki dane tempData nie zostaną zużyte.
  • Czy aplikacja działa w farmie serwerów na wielu serwerach? Jeśli tak, nie ma dodatkowej konfiguracji wymaganej do korzystania z cookie dostawcy TempData poza ochroną danych (zobacz ASP.NET Core Data Protection Overview and Key Storage providers (Omówienie podstawowego ochrony danych i dostawców magazynu kluczy).

Większość klientów internetowych, takich jak przeglądarki internetowe, wymusza limity maksymalnego rozmiaru każdego cookie i łączną liczbę cookies. W przypadku korzystania z dostawcy cookie TempData sprawdź, czy aplikacja nie przekroczy tych limitów. Rozważ łączny rozmiar danych. Uwzględnij wzrost rozmiaru cookie ze względu na szyfrowanie i fragmentowanie.

Konfigurowanie dostawcy TempData

Dostawca cookieTempData oparty na protokole TempData jest domyślnie włączony.

Aby włączyć dostawcę TempData opartego na sesji, użyj AddSessionStateTempDataProvider metody rozszerzenia. Wymagane jest tylko jedno wywołanie 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();
    });
}

Ciągi zapytań

Ograniczoną ilość danych można przekazać z jednego żądania do innego, dodając je do ciągu zapytania nowego żądania. Jest to przydatne w przypadku przechwytywania stanu w trwały sposób, który umożliwia udostępnianie linków ze stanem osadzonym za pośrednictwem poczty e-mail lub sieci społecznościowych. Ponieważ ciągi zapytania adresu URL są publiczne, nigdy nie używaj ciągów zapytań dla poufnych danych.

Oprócz niezamierzonego udostępniania, w tym danych w ciągach zapytań, można uwidocznić aplikację na ataki między witrynami w celu fałszerowania (CSRF ). Każdy zachowany stan sesji musi chronić przed atakami CSRF. Aby uzyskać więcej informacji, zobacz Zapobieganie atakom z fałszowaniem żądań międzywitrynowych (XSRF/CSRF) na platformie ASP.NET Core.

Ukryte pola

Dane można zapisywać w ukrytych polach formularza i publikować je ponownie w następnym żądaniu. Jest to często spotykane w formularzach wielostronicowych. Ponieważ klient może potencjalnie manipulować danymi, aplikacja musi zawsze ponownie modyfikować dane przechowywane w ukrytych polach.

HttpContext.Items

Kolekcja HttpContext.Items służy do przechowywania danych podczas przetwarzania pojedynczego żądania. Zawartość kolekcji jest odrzucana po przetworzeniu żądania. Kolekcja Items jest często używana do zezwalania składnikom lub oprogramowaniem pośredniczącemu na komunikowanie się, gdy działają w różnych punktach w czasie podczas żądania i nie mają bezpośredniego sposobu przekazywania parametrów.

W poniższym przykładzie oprogramowanie pośredniczące dodaje isVerified do kolekcji 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"]}");
        });
    });
}

W przypadku oprogramowania pośredniczącego, które jest używane tylko w jednej aplikacji, stałe string klucze są akceptowalne. Oprogramowanie pośredniczące współużytkowane między aplikacjami powinno używać unikatowych kluczy obiektów, aby uniknąć kolizji kluczy. W poniższym przykładzie pokazano, jak używać unikatowego klucza obiektu zdefiniowanego w klasie oprogramowania pośredniczącego:

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

Inny kod może uzyskać dostęp do wartości przechowywanej przy HttpContext.Items użyciu klucza uwidocznionego przez klasę oprogramowania pośredniczącego:

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

Takie podejście ma również zaletę wyeliminowania używania ciągów kluczy w kodzie.

Cache

Buforowanie to wydajny sposób przechowywania i pobierania danych. Aplikacja może kontrolować okres istnienia buforowanych elementów. Aby uzyskać więcej informacji, zobacz Buforowanie odpowiedzi w programie ASP.NET Core.

Dane buforowane nie są skojarzone z określonym żądaniem, użytkownikiem ani sesją. Nie buforuj danych specyficznych dla użytkownika, które mogą być pobierane przez inne żądania użytkownika.

Aby buforować dane w całej aplikacji, zobacz Buforowanie w pamięci w ASP.NET Core.

Typowe błędy

  • "Nie można rozpoznać usługi dla typu "Microsoft.Extensions". Buforowanie. Distributed.IDistributedCache" podczas próby aktywowania "Microsoft.AspNetCore.Session.DistributedSessionStore".

    Jest to zwykle spowodowane niepowodzeniem konfigurowania co najmniej jednej IDistributedCache implementacji. Aby uzyskać więcej informacji, zobacz Buforowanie rozproszone w ASP.NET Core i Pamięci podręcznej w pamięci w ASP.NET Core.

Jeśli oprogramowanie pośredniczące sesji nie będzie utrwalać sesji:

  • Oprogramowanie pośredniczące rejestruje wyjątek, a żądanie jest kontynuowane normalnie.
  • Prowadzi to do nieprzewidywalnego zachowania.

Oprogramowanie pośredniczące sesji może zakończyć się niepowodzeniem, jeśli magazyn kopii zapasowych nie jest dostępny. Na przykład użytkownik przechowuje koszyk w sesji. Użytkownik dodaje element do koszyka, ale zatwierdzenie kończy się niepowodzeniem. Aplikacja nie wie o awarii, dlatego zgłasza użytkownikowi, że element został dodany do koszyka, co nie jest prawdą.

Zalecaną metodą sprawdzania błędów jest wywołanie await feature.Session.CommitAsync podczas zapisywania aplikacji w sesji. CommitAsync zgłasza wyjątek, jeśli magazyn zapasowy jest niedostępny. W przypadku CommitAsync niepowodzenia aplikacja może przetworzyć wyjątek. LoadAsync zgłasza te same warunki, gdy magazyn danych jest niedostępny.

Dodatkowe zasoby

Hostowanie aplikacji ASP.NET Core na farmie internetowej