ASP.NET Core 中的工作階段與應用程式狀態Session and app state in ASP.NET Core

作者:Rick AndersonSteve SmithDiana LaRoseLuke LathamBy Rick Anderson, Steve Smith, Diana LaRose, and Luke Latham

HTTP 是無狀態的通訊協定。HTTP is a stateless protocol. 若不採取其他步驟,HTTP 要求是獨立的訊息,不會保留使用者的值或應用程式狀態。Without taking additional steps, HTTP requests are independent messages that don't retain user values or app state. 本文描述數種方法來在要求之間保留使用者資料和應用程式狀態。This article describes several approaches to preserve user data and app state between requests.

檢視或下載範例程式碼 (英文) (如何下載)View or download sample code (how to download)

狀態管理State management

可以使用數種方法來儲存狀態。State can be stored using several approaches. 本主題稍後將提供每種方法的描述。Each approach is described later in this topic.

儲存方法Storage approach 儲存機制Storage mechanism
CookieCookies HTTP cookie (可能包含使用伺服器端應用程式程式碼所儲存的資料)HTTP cookies (may include data stored using server-side app code)
工作階段狀態Session state HTTP Cookie 和伺服器端應用程式程式碼HTTP cookies and server-side app code
TempDataTempData HTTP Cookie 或工作階段狀態HTTP cookies or session state
查詢字串Query strings HTTP 查詢字串HTTP query strings
隱藏欄位Hidden fields HTTP 表單欄位HTTP form fields
HttpContext.ItemsHttpContext.Items 伺服器端應用程式程式碼Server-side app code
快取Cache 伺服器端應用程式程式碼Server-side app code
相依性插入Dependency Injection 伺服器端應用程式程式碼Server-side app code

CookieCookies

Cookie 會在要求之間儲存資料。Cookies store data across requests. 因為 Cookie 會隨著每個要求傳送,所以其大小應該保持最小。Because cookies are sent with every request, their size should be kept to a minimum. 在理想情況下,應該只有識別碼儲存在 Cookie 中,而資料由應用程式儲存。Ideally, only an identifier should be stored in a cookie with the data stored by the app. 大部分的瀏覽器將 Cookie 大小限制為 4096 個位元組。Most browsers restrict cookie size to 4096 bytes. 每個網域只有數量有限的 Cookie 可供使用。Only a limited number of cookies are available for each domain.

由於 Cookie 可能會遭到竄改,因此必須由應用程式加以驗證。Because cookies are subject to tampering, they must be validated by the app. 使用者可以刪除 Cookie,而且 Cookie 會在用戶端上過期。Cookies can be deleted by users and expire on clients. 不過,Cookie 通常是在用戶端上資料持續性最持久的形式。However, cookies are generally the most durable form of data persistence on the client.

Cookie 通常可用於個人化,其中內容會針對已知的使用者自訂。Cookies are often used for personalization, where content is customized for a known user. 在大部分情況下,只會識別使用者,而未加以驗證。The user is only identified and not authenticated in most cases. Cookie 可以儲存使用者的名稱、帳戶名稱或唯一使用者識別碼 (例如 GUID)。The cookie can store the user's name, account name, or unique user ID (such as a GUID). 您接著可以使用 Cookie 來存取使用者的個人化設定,例如其慣用網站的背景色彩。You can then use the cookie to access the user's personalized settings, such as their preferred website background color.

在發出 Cookie 及處理隱私權顧慮時,請留意歐盟一般資料保護規定 (GDPR)Be mindful of the European Union General Data Protection Regulations (GDPR) when issuing cookies and dealing with privacy concerns. 如需詳細資訊,請參閱 ASP.NET Core 中的一般資料保護規定 (GDPR) 支援For more information, see General Data Protection Regulation (GDPR) support in ASP.NET Core.

工作階段狀態Session state

工作階段狀態是用來在使用者瀏覽 Web 應用程式時存放使用者資料的 ASP.NET Core 情節。Session state is an ASP.NET Core scenario for storage of user data while the user browses a web app. 工作階段狀態使用應用程式所維護的存放區,在用戶端的要求之間保存資料。Session state uses a store maintained by the app to persist data across requests from a client. 工作階段資料受到快取的支援,並被視為暫時資料—網站沒有工作階段資料應該也會繼續運作。The session data is backed by a cache and considered ephemeral data—the site should continue to function without the session data. 重要應用程式資料應該儲存在使用者資料庫,並只在工作階段中快取以獲得效能最佳化。Critical application data should be stored in the user database and cached in session only as a performance optimization.

注意

SignalR 應用程式中不支援工作階段,因為 SignalR 中樞可獨立於 HTTP 內容之外而執行。Session isn't supported in SignalR apps because a SignalR Hub may execute independent of an HTTP context. 例如,當長時間輪詢要求由中樞維持開啟,超過要求的 HTTP 內容存留期時,便可能發生此情況。For example, this can occur when a long polling request is held open by a hub beyond the lifetime of the request's HTTP context.

ASP.NET Core 可維護工作階段狀態,方法是提供包含工作階段識別碼的 Cookie 給用戶端,以便將其隨著每個要求傳送至應用程式。ASP.NET Core maintains session state by providing a cookie to the client that contains a session ID, which is sent to the app with each request. 應用程式則使用工作階段識別碼來擷取工作階段資料。The app uses the session ID to fetch the session data.

工作階段狀態表現下列行為:Session state exhibits the following behaviors:

  • 因為工作階段 Cookie 是瀏覽器所特有,所以不會跨瀏覽器共用工作階段。Because the session cookie is specific to the browser, sessions aren't shared across browsers.
  • 在瀏覽器工作階段結束時,會刪除工作階段 Cookie。Session cookies are deleted when the browser session ends.
  • 如果收到過期工作階段的 Cookie,則會建立使用相同工作階段 Cookie 的新工作階段。If a cookie is received for an expired session, a new session is created that uses the same session cookie.
  • 並不會保留空白工作階段—必須在工作階段中設定至少一個值,才能在要求之間保存工作階段。Empty sessions aren't retained—the session must have at least one value set into it to persist the session across requests. 不保留工作階段時,會為每個新的要求產生新的工作階段識別碼。When a session isn't retained, a new session ID is generated for each new request.
  • 應用程式會在最後一個要求之後,保留工作階段一段有限的時間。The app retains a session for a limited time after the last request. 應用程式會設定工作階段逾時或使用預設值 20 分鐘。The app either sets the session timeout or uses the default value of 20 minutes. 工作階段狀態適合用來儲存特定工作階段特定的使用者資料,但資料不需要在工作階段之間永久儲存的情況。Session state is ideal for storing user data that's specific to a particular session but where the data doesn't require permanent storage across sessions.
  • 工作階段資料會在呼叫 ISession.Clear 實作或工作階段過期時刪除。Session data is deleted either when the ISession.Clear implementation is called or when the session expires.
  • 沒有任何預設機制可通知應用程式程式碼,用戶端瀏覽器已關閉,或工作階段 Cookie 遭到刪除或在用戶端上已過期。There's no default mechanism to inform app code that a client browser has been closed or when the session cookie is deleted or expired on the client.
  • ASP.NET Core MVC 和 Razor Pages 範本包含對一般資料保護規定 (GDPR) 的支援。The ASP.NET Core MVC and Razor pages templates include support for General Data Protection Regulation (GDPR). 工作階段狀態 Cookie 未預設標示為必要項目,因此工作階段狀態無法運作,除網站訪客允許追蹤。Session state cookies aren't marked essential by default, so session state isn't functional unless tracking is permitted by the site visitor. 如需詳細資訊,請參閱 ASP.NET Core 中的一般資料保護規定(GDPR)支援For more information, see ASP.NET Core 中的一般資料保護規定(GDPR)支援.

警告

請勿將敏感性資料存放在工作階段狀態。Don't store sensitive data in session state. 使用者可能不會關閉瀏覽器,並清除工作階段 Cookie。The user might not close the browser and clear the session cookie. 某些瀏覽器會在瀏覽器視窗之間維護有效的工作階段 Cookie。Some browsers maintain valid session cookies across browser windows. 工作階段可能無法限制為單一使用者—下一位使用者可能會繼續使用相同的工作階段 Cookie 瀏覽應用程式。A session might not be restricted to a single user—the next user might continue to browse the app with the same session cookie.

記憶體中快取提供者會將工作階段資料存放在應用程式所在伺服器的記憶體中。The in-memory cache provider stores session data in the memory of the server where the app resides. 在伺服器陣列案例中:In a server farm scenario:

設定工作階段狀態Configure session state

Microsoft.AspNetCore.Session 套件隨附於 Microsoft.AspNetCore.App metapackage,提供用來管理工作階段狀態的中介軟體。The Microsoft.AspNetCore.Session package, which is included in the Microsoft.AspNetCore.App metapackage, provides middleware for managing session state. 若要啟用工作階段中介軟體,Startup 必須包含:To enable the session middleware, Startup must contain:

下列程式碼示範如何設定記憶體內部工作階段提供者,以及 IDistributedCache 的預設記憶體中實作。The following code shows how to set up the in-memory session provider with a default in-memory implementation of IDistributedCache:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDistributedMemoryCache();

        services.AddSession(options =>
        {
            // Set a short timeout for easy testing.
            options.IdleTimeout = TimeSpan.FromSeconds(10);
            options.Cookie.HttpOnly = true;
            // Make the session cookie essential
            options.Cookie.IsEssential = true;
        });

        services.AddMvc()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseSession();
        app.UseHttpContextItemsMiddleware();
        app.UseMvc();
    }
}

中介軟體的順序很重要。The order of middleware is important. 在上述範例中,如果在 UseMvc 之後叫用 UseSession,則會發生 InvalidOperationException 例外狀況。In the preceding example, an InvalidOperationException exception occurs when UseSession is invoked after UseMvc. 如需詳細資訊,請參閱中介軟體順序For more information, see Middleware Ordering.

設定工作階段狀態之後便可以使用 HttpContext.SessionHttpContext.Session is available after session state is configured.

呼叫 UseSession 之前無法存取 HttpContext.SessionHttpContext.Session can't be accessed before UseSession has been called.

應用程式開始寫入回應資料流之後,無法建立具有新工作階段 Cookie 的新工作階段。A new session with a new session cookie can't be created after the app has begun writing to the response stream. 例外狀況會記錄在 Web 伺服器記錄檔中,而不會顯示在瀏覽器。The exception is recorded in the web server log and not displayed in the browser.

非同步載入工作階段狀態Load session state asynchronously

只有在 TryGetValueSetRemove 方法之前明確呼叫 ISession.LoadAsync 方法時,ASP.NET Core 中的預設工作階段提供者才會從基礎 IDistributedCache 備份存放區以非同步方式載入工作階段記錄。The default session provider in ASP.NET Core loads session records from the underlying IDistributedCache backing store asynchronously only if the ISession.LoadAsync method is explicitly called before the TryGetValue, Set, or Remove methods. 如果並未先呼叫 LoadAsync,則基礎工作階段記錄會同步載入,這可能大規模地為效能帶來負面影響。If LoadAsync isn't called first, the underlying session record is loaded synchronously, which can incur a performance penalty at scale.

若要讓應用程式強制執行此模式,請使用未在 TryGetValueSetRemove 之前呼叫 LoadAsync 方法時擲回例外狀況的版本來包裝 DistributedSessionStoreDistributedSession 實作。To have apps enforce this pattern, wrap the DistributedSessionStore and DistributedSession implementations with versions that throw an exception if the LoadAsync method isn't called before TryGetValue, Set, or Remove. 請在服務容器中註冊已包裝的版本。Register the wrapped versions in the services container.

工作階段選項Session options

若要覆寫工作階段的預設值,請使用 SessionOptionsTo override session defaults, use SessionOptions.

選項Option 描述Description
CookieCookie 決定用來建立 Cookie 的設定。Determines the settings used to create the cookie. Name 預設為 SessionDefaults.CookieName (.AspNetCore.Session)。Name defaults to SessionDefaults.CookieName (.AspNetCore.Session). Path 預設為 SessionDefaults.CookiePath (/)。Path defaults to SessionDefaults.CookiePath (/). SameSite 預設為 SameSiteMode.Lax (1)。SameSite defaults to SameSiteMode.Lax (1). HttpOnly 預設為 trueHttpOnly defaults to true. IsEssential 預設為 falseIsEssential defaults to false.
IdleTimeoutIdleTimeout IdleTimeout 指出工作階段可以閒置多久,之後才會放棄它的內容。The IdleTimeout indicates how long the session can be idle before its contents are abandoned. 每個工作階段存取都會重設逾時。Each session access resets the timeout. 此設定只適用於工作階段的內容,而非 Cookie。This setting only applies to the content of the session, not the cookie. 預設值是 20 分鐘。The default is 20 minutes.
IOTimeoutIOTimeout 從存放區載入工作階段,或將它認可回到存放區時,所允許的時間長度上限。The maximum amount of time allowed to load a session from the store or to commit it back to the store. 此設定只可能適用於非同步作業。This setting may only apply to asynchronous operations. 此逾時可以使用 InfiniteTimeSpan 來停用。This timeout can be disabled using InfiniteTimeSpan. 預設為 1 分鐘。The default is 1 minute.

工作階段使用 Cookie 來追蹤和識別來自單一瀏覽器的要求。Session uses a cookie to track and identify requests from a single browser. 此 Cookie 預設名為 .AspNetCore.Session,並使用路徑 /By default, this cookie is named .AspNetCore.Session, and it uses a path of /. 由於 Cookie 預設值未指定網域,因此它不會提供給頁面上的用戶端指令碼 (因為 HttpOnly 預設為 true)。Because the cookie default doesn't specify a domain, it isn't made available to the client-side script on the page (because HttpOnly defaults to true).

若要覆寫 Cookie 工作階段的預設值,請使用 SessionOptionsTo override cookie session defaults, use SessionOptions:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDistributedMemoryCache();

    services.AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddSession(options =>
    {
        options.Cookie.Name = ".AdventureWorks.Session";
        options.IdleTimeout = TimeSpan.FromSeconds(10);
        options.Cookie.IsEssential = true;
    });
}

應用程式會使用 IdleTimeout 屬性,判斷工作階段可以在放棄伺服器快取中的內容之前閒置多長時間。The app uses the IdleTimeout property to determine how long a session can be idle before its contents in the server's cache are abandoned. 這個屬性與 Cookie 到期日無關。This property is independent of the cookie expiration. 透過工作階段中介軟體傳遞的每個要求都會重設逾時。Each request that passes through the Session Middleware resets the timeout.

工作階段狀態為「非鎖定」。Session state is non-locking. 如果兩個要求同時嘗試修改工作階段的內容,則最後一個要求會覆寫第一個要求。If two requests simultaneously attempt to modify the contents of a session, the last request overrides the first. Session 會實作為「一致性工作階段」,這表示所有內容會都儲存在一起。Session is implemented as a coherent session, which means that all the contents are stored together. 當兩個要求試圖修改不同的工作階段值時,最後一個要求可能會覆寫第一個要求所做的工作階段變更。When two requests seek to modify different session values, the last request may override session changes made by the first.

設定和取得工作階段值Set and get Session values

工作階段狀態的存取,是從 Razor Pages PageModel 類別,或 MVC Controller 類別搭配 HttpContext.SessionSession state is accessed from a Razor Pages PageModel class or MVC Controller class with HttpContext.Session. 這個屬性是 ISession 實作。This property is an ISession implementation.

ISession 實作提供數個延伸模組來設定和擷取整數和字串值。The ISession implementation provides several extension methods to set and retrieve integer and string values. 當專案參考 Microsoft.AspNetCore.Http.Extensions 套件時,擴充方法位於 Microsoft.AspNetCore.Http 命名空間 (新增 using Microsoft.AspNetCore.Http; 陳述式即可取得擴充方法的存取權)。The extension methods are in the Microsoft.AspNetCore.Http namespace (add a using Microsoft.AspNetCore.Http; statement to gain access to the extension methods) when the Microsoft.AspNetCore.Http.Extensions package is referenced by the project. Microsoft.AspNetCore.App 中繼套件包含兩個套件。Both packages are included in the Microsoft.AspNetCore.App metapackage.

ISession 擴充方法:ISession extension methods:

下列範例會在 Razor Pages 頁面中,擷取 IndexModel.SessionKeyName 索引鍵的工作階段值 (範例應用程式中的 _Name):The following example retrieves the session value for the IndexModel.SessionKeyName key (_Name in the sample app) in a Razor Pages page:

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

...

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

下列範例示範如何設定及取得整數和字串:The following example shows how to set and get an integer and a string:

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

若要啟用分散式快取案例,即使是使用記憶體中快取時,都必須序列化所有工作階段資料。All session data must be serialized to enable a distributed cache scenario, even when using the in-memory cache. 已提供最小字串及數字的序列化程式 (請參閱 ISession 的方法和擴充方法)。Minimal string and number serializers are provided (see the methods and extension methods of ISession). 複雜類型必須由使用者使用另一個機制加以序列化,例如 JSON。Complex types must be serialized by the user using another mechanism, such as JSON.

新增下列擴充方法,即可設定和取得可序列化物件:Add the following extension methods to set and get serializable objects:

public static class SessionExtensions
{
    public static void Set<T>(this ISession session, string key, T value)
    {
        session.SetString(key, JsonConvert.SerializeObject(value));
    }

    public static T Get<T>(this ISession session, string key)
    {
        var value = session.GetString(key);

        return value == null ? default(T) : 
            JsonConvert.DeserializeObject<T>(value);
    }
}

下列範例示範如何使用擴充方法來設定和取得可序列化物件:The following example shows how to set and get a serializable object with the extension methods:

// Requires you add the Set and Get extension method mentioned in the topic.
if (HttpContext.Session.Get<DateTime>(SessionKeyTime) == default(DateTime))
{
    HttpContext.Session.Set<DateTime>(SessionKeyTime, currentTime);
}

TempDataTempData

ASP.NET Core 會公開 Razor Pages TempData或控制器TempDataASP.NET Core exposes the Razor Pages TempData or Controller TempData. 這個屬性會儲存資料,直到讀取另一個要求為止。This property stores data until it's read in another request. 在要求結束時,可以使用保留(字串)查看(字串)方法來檢查資料,而不需要刪除。Keep(String) and Peek(string) methods can be used to examine the data without deletion at the end of the request. Keep ()會標示字典中的所有專案以進行保留。Keep() marks all items in the dictionary for retention. TempData當需要多個單一要求的資料時,對於重新導向而言特別有用。TempData is particularly useful for redirection when data is required for more than a single request. TempData是由使用TempData cookie 或會話狀態的提供者所執行。TempData is implemented by TempData providers using either cookies or session state.

TempData 範例TempData samples

請考慮下列會建立客戶的頁面:Consider the following page that creates a customer:

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

會顯示TempData["Message"]下列頁面:The following page displays TempData["Message"]:

@page
@model IndexModel

<h1>Peek Contacts</h1>

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

@*Content removed for brevity.*@

在上述標記中,在要求結束時,TempData["Message"]刪除,因為Peek會使用。In the preceding markup, at the end of the request, TempData["Message"] is not deleted because Peek is used. 重新整理頁面會TempData["Message"]顯示。Refreshing the page displays TempData["Message"].

下列標記與上述程式碼類似,但會使用Keep來保留要求結尾的資料:The following markup is similar to the preceding code, but uses Keep to preserve the data at the end of the request:

@page
@model IndexModel

<h1>Contacts Keep</h1>

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

@*Content removed for brevity.*@

IndexPeekIndexKeep頁面之間流覽並不TempData["Message"]會刪除。Navigating between the IndexPeek and IndexKeep pages won't delete TempData["Message"].

下列程式碼會TempData["Message"]顯示,但在要求結束時, TempData["Message"]會刪除:The following code displays TempData["Message"], but at the end of the request, TempData["Message"] is deleted:

@page
@model IndexModel

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

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

@*Content removed for brevity.*@

TempData 提供者TempData providers

預設會使用以 Cookie 為基礎的 TempData 提供者,以將 TempData 儲存在 Cookie 中。The cookie-based TempData provider is used by default to store TempData in cookies.

Cookie 資料是使用 IDataProtector 加密,並使用 Base64UrlTextEncoder 編碼,然後分成區塊。The cookie data is encrypted using IDataProtector, encoded with Base64UrlTextEncoder, then chunked. 因為 Cookie 分成區塊,所以不適用 ASP.NET Core 1.x 中找到的 Cookie 大小限制。Because the cookie is chunked, the single cookie size limit found in ASP.NET Core 1.x doesn't apply. Cookie 資料不會壓縮,因為壓縮加密資料可能會導致安全性問題,例如 CRIMEBREACH 攻擊。The cookie data isn't compressed because compressing encrypted data can lead to security problems such as the CRIME and BREACH attacks. 如需 Cookie 架構 TempData 提供者的詳細資訊,請參閱 CookieTempDataProviderFor more information on the cookie-based TempData provider, see CookieTempDataProvider.

選擇 TempData 提供者Choose a TempData provider

選擇 TempData 提供者涉及數項考量,例如:Choosing a TempData provider involves several considerations, such as:

  1. 應用程式已經使用工作階段狀態了嗎?Does the app already use session state? 如果是的話,使用工作階段狀態 TempData 提供者沒有額外的應用程式成本 (除了資料的大小之外)。If so, using the session state TempData provider has no additional cost to the app (aside from the size of the data).
  2. 應用程式是否盡量只將 TempData 用於相對少量的資料 (最多 500 個位元組)?Does the app use TempData only sparingly for relatively small amounts of data (up to 500 bytes)? 如果是的話,Cookie TempData 提供者將對包含 TempData 的每個要求新增少量成本。If so, the cookie TempData provider adds a small cost to each request that carries TempData. 如果不是的話,則工作階段狀態 TempData 提供者可能有助於避免在每個要求中來回傳送大量資料,直到取用 TempData 為止。If not, the session state TempData provider can be beneficial to avoid round-tripping a large amount of data in each request until the TempData is consumed.
  3. 應用程式在伺服器陣列中的多部伺服器上執行?Does the app run in a server farm on multiple servers? 如果是的話,不需要額外組態,即可在資料保護之外使用 Cookie TempData 提供者 (請參閱 ASP.NET Core 資料保護金鑰儲存提供者)。If so, there's no additional configuration required to use the cookie TempData provider outside of Data Protection (see ASP.NET Core 資料保護 and Key storage providers).

注意

大部分的 Web 用戶端 (例如網頁瀏覽器) 會強制執行每個 Cookie 的大小上限、Cookie 總數或這兩者的限制。Most web clients (such as web browsers) enforce limits on the maximum size of each cookie, the total number of cookies, or both. 使用 Cookie TempData 提供者時,請確認應用程式不會超過這些限制。When using the cookie TempData provider, verify the app won't exceed these limits. 請考慮資料的大小總計。Consider the total size of the data. 請考慮因為加密和區塊處理而增加的 Cookie 大小。Account for increases in cookie size due to encryption and chunking.

設定 TempData 提供者Configure the TempData provider

預設會啟用 Cookie 架構 TempData 提供者。The cookie-based TempData provider is enabled by default.

若要啟用以工作階段為基礎的 TempData 提供者,請使用 AddSessionStateTempDataProvider 擴充方法:To enable the session-based TempData provider, use the AddSessionStateTempDataProvider extension method:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
        .AddSessionStateTempDataProvider();

    services.AddSession();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();
    app.UseSession();
    app.UseMvc();
}

中介軟體的順序很重要。The order of middleware is important. 在上述範例中,如果在 UseMvc 之後叫用 UseSession,則會發生 InvalidOperationException 例外狀況。In the preceding example, an InvalidOperationException exception occurs when UseSession is invoked after UseMvc. 如需詳細資訊,請參閱中介軟體順序For more information, see Middleware Ordering.

重要

如果目標為 .NET Framework 且使用以工作階段為基礎的 TempData 提供者,請將 Microsoft.AspNetCore.Session 套件新增至專案。If targeting .NET Framework and using the session-based TempData provider, add the Microsoft.AspNetCore.Session package to the project.

查詢字串Query strings

可以將數量有限的資料從某個要求傳遞到另一個要求,方法是將其新增至新要求的查詢字串。A limited amount of data can be passed from one request to another by adding it to the new request's query string. 這對於以持續方式擷取狀態很有用,可讓內嵌狀態的連結透過電子郵件或社交網路共用。This is useful for capturing state in a persistent manner that allows links with embedded state to be shared through email or social networks. 因為 URL 查詢字串為公用,所以請絕對不要使用查詢字串來處理敏感性資料。Because URL query strings are public, never use query strings for sensitive data.

除了非預期的共用之外,在查詢字串中包括資料還可能會為跨網站偽造要求 (CSRF) 攻擊創造機會,這些攻擊可能會誘騙使用者在驗證時瀏覽惡意網站。In addition to unintended sharing, including data in query strings can create opportunities for Cross-Site Request Forgery (CSRF) attacks, which can trick users into visiting malicious sites while authenticated. 然後,攻擊者可能會竊取應用程式中的使用者資料,或代表使用者採取惡意動作。Attackers can then steal user data from the app or take malicious actions on behalf of the user. 任何保留的應用程式或工作階段狀態必須防範 CSRF 攻擊。Any preserved app or session state must protect against CSRF attacks. 如需詳細資訊,請參閱防止跨站台要求偽造 (XSRF/CSRF) 攻擊For more information, see Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks.

隱藏欄位Hidden fields

資料可以儲存在隱藏的表單欄位,並貼回下一個要求。Data can be saved in hidden form fields and posted back on the next request. 這在多頁表單中很常見。This is common in multi-page forms. 因為用戶端可能會竄改資料,所以應用程式必須一律重新驗證存放在隱藏欄位中的資料。Because the client can potentially tamper with the data, the app must always revalidate the data stored in hidden fields.

HttpContext.ItemsHttpContext.Items

HttpContext.Items 集合用來在處理單一要求時存放資料。The HttpContext.Items collection is used to store data while processing a single request. 集合的內容會在每個要求處理之後捨棄。The collection's contents are discarded after a request is processed. 當元件或中介軟體在要求期間的不同時間點運作,而且沒有可傳遞參數的直接方式時,Items 集合經常用來允許元件或中介軟體進行通訊。The Items collection is often used to allow components or middleware to communicate when they operate at different points in time during a request and have no direct way to pass parameters.

在下列範例中,中介軟體會將 isVerified 新增至 Items 集合。In the following example, middleware adds isVerified to the Items collection.

app.Use(async (context, next) =>
{
    // perform some verification
    context.Items["isVerified"] = true;
    await next.Invoke();
});

稍後在管線中,另一個中介軟體可以存取 isVerified 的值:Later in the pipeline, another middleware can access the value of isVerified:

app.Run(async (context) =>
{
    await context.Response.WriteAsync($"Verified: {context.Items["isVerified"]}");
});

如果是只由單一應用程式使用的中介軟體,可接受 string 索引鍵。For middleware that's only used by a single app, string keys are acceptable. 應用程式執行個體之間共用的中介軟體,應該使用唯一的物件索引鍵,來避免索引鍵衝突。Middleware shared between app instances should use unique object keys to avoid key collisions. 下列範例示範如何使用中介軟體類別中定義的唯一物件索引鍵:The following example shows how to use a unique object key defined in a middleware class:

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 builder)
    {
        return builder.UseMiddleware<HttpContextItemsMiddleware>();
    }
}

其他程式碼可以使用中介軟體類別所公開的索引鍵,來存取 HttpContext.Items 中儲存的值:Other code can access the value stored in HttpContext.Items using the key exposed by the middleware class:

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

此方法也有在程式碼中消除使用索引鍵字串的優點。This approach also has the advantage of eliminating the use of key strings in the code.

快取Cache

快取是儲存和擷取資料的有效方式。Caching is an efficient way to store and retrieve data. 應用程式可以控制快取項目的存留期。The app can control the lifetime of cached items.

快取的資料未與特定要求、使用者或工作階段建立關聯。Cached data isn't associated with a specific request, user, or session. 請小心不要快取可能由其他使用者要求所擷取的特定使用者資料。Be careful not to cache user-specific data that may be retrieved by other users' requests.

如需詳細資訊,請參閱 ASP.NET Core 中的回應快取For more information, see ASP.NET Core 中的回應快取.

相依性插入Dependency Injection

請使用相依性插入將資料提供給所有使用者:Use Dependency Injection to make data available to all users:

  1. 定義包含資料的服務。Define a service containing the data. 例如,已定義名為 MyAppData 的類別:For example, a class named MyAppData is defined:

    public class MyAppData
    {
        // Declare properties and methods
    }
    
  2. 將服務類別新增至 Startup.ConfigureServicesAdd the service class to Startup.ConfigureServices:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<MyAppData>();
    }
    
  3. 取用資料服務類別:Consume the data service class:

    public class IndexModel : PageModel
    {
        public IndexModel(MyAppData myService)
        {
            // Do something with the service
            //    Examples: Read data, store in a field or property
        }
    }
    

常見的錯誤Common errors

  • 「嘗試啟動 'Microsoft.AspNetCore.Session.DistributedSessionStore' 時,無法解析類型 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' 。」"Unable to resolve service for type 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' while attempting to activate 'Microsoft.AspNetCore.Session.DistributedSessionStore'."

    這種情形通常是因為無法設定至少一個 IDistributedCache 實作。This is usually caused by failing to configure at least one IDistributedCache implementation. 如需詳細資訊,請參閱 ASP.NET Core 中的分散式快取ASP.NET Core 中的記憶體快取For more information, see ASP.NET Core 中的分散式快取 and ASP.NET Core 中的記憶體快取.

  • 如果工作階段中介軟體無法持續存在於工作階段 (例如,備份存放區無法使用),中介軟體會記錄例外狀況,而要求會照常繼續進行。In the event that the session middleware fails to persist a session (for example, if the backing store isn't available), the middleware logs the exception and the request continues normally. 這會導致無法預期的行為。This leads to unpredictable behavior.

    例如,使用者在工作階段中存放購物車。For example, a user stores a shopping cart in session. 使用者在購物車新增一個項目,但認可失敗。The user adds an item to the cart but the commit fails. 應用程式未察覺到失敗,因此它向使用者報告項目已新增至購物車,但這並不正確。The app doesn't know about the failure so it reports to the user that the item was added to their cart, which isn't true.

    檢查是否有錯誤的建議方法是,當應用程式完成寫入至工作階段後,從應用程式程式碼呼叫 await feature.Session.CommitAsync();The recommended approach to check for errors is to call await feature.Session.CommitAsync(); from app code when the app is done writing to the session. 備份存放區無法使用時,CommitAsync 會擲回例外狀況。CommitAsync throws an exception if the backing store is unavailable. 如果 CommitAsync 失敗,應用程式可以處理例外狀況。If CommitAsync fails, the app can process the exception. LoadAsync 在無法使用資料存放區的相同情況下會擲回。LoadAsync throws under the same conditions where the data store is unavailable.

其他資源Additional resources

在 Web 伺服陣列上裝載 ASP.NET Core