在ASP.NET Core中,由多種途徑可以對應用程式狀態進行管理,使用哪種途徑,由檢索狀態的時機和方式決定。
應用程式狀態指的是用於描述當前狀況的任意資料。包括全域性和使用者特有的資料。
開發人員可以根據不同的因素來選擇不同的方式儲存狀態資料:
資料需要儲存多久
資料有多大
資料的格式是什麼
資料是否可以序列化
資料有多敏感
資料能否儲存在客戶端
1.可選方式
1.HttpContext.Items
當資料僅用於一個請求中時,用Items集合儲存時最好的方式。資料將在每個請求結束之後丟棄。它是元件和中介軟體在一個請求中的不同時間點金總互相通訊的最佳手段。
HttpContext抽象提供了一個簡單的IDictionary<object,object>型別的字典集合,就是Items。在每個請求中,這個集合從HttpRequest開始就可以使用,直到請求結束丟棄。要想存取集合,可以直接賦值和根據鍵查詢。
app.Use(async (context,next) => { context.Items["isExist"] = true; await next.Invoke(); }); //在之後的管道查詢值 app.Run(async (context) => { await context.Response.WriteAsync("Is Exist:"+context.Items["isExist"]); });
2.QueryString 和 Post
在查詢字串(QueryString )中新增值,或利用Post傳送資料,可以將一個請求的狀態資料提供給另一個請求。這不適合敏感資料,因為這需要將資料傳送到客戶端,然後再傳送給伺服器。這種方法也只適用於少量資料。使用者提交的資料是無法預期的,帶查詢字串的網址很容易洩露,所以要避免跨網站請求偽裝攻擊(CSRF)。
3.Cookies
與狀態有關的小量資料可以儲存在Cookies中。他們會隨每次請求被髮送到客戶端。應該只使用一個識別符號,真正的資料儲存在服務端,服務端的資料與這個標識關聯。
4.Session
會話儲存依靠一個基於Cookie的識別符號來訪問與給定瀏覽器相關的會話資料。一個會話可以與多個Cookie關聯。
5.Cache
快取提供了一種方法,用自定義的鍵對應用程式資料進行儲存和檢索。它提供了一套基於時間和其他因素使快取過期的規則。
6.其他
還可以使用EF和資料庫等進行儲存應用程式狀態。
2.使用Session
首先要安裝Microsoft.AspNetCore.Session安裝包。然後在Startup類中配置。Session是基於IDistributedCache構建的,因此必須先配置好Session,否則會報錯。
services.AddDistributedMemoryCache(); services.AddSession(options => { options.Cookie.Name = "Test.Session"; options.IdleTimeout = TimeSpan.FromSeconds(10); });
ASP.NET 提供了IDistributedCache的多種實現,in-memory是其中之一。上面採用in-memory,需要先安裝Microsoft.Extensions.Caching.Memory,然後新增上面程式碼。
最後在Configure中呼叫 app.UseSession(),需要在app.UseMvc使用之前呼叫。
(1)實現細節
Session利用一個cookie來跟蹤和區分不同瀏覽器發出的請求。預設情況下,這個cookie被命名為“.ASP.Session”,並使用路徑“/”。預設情況下,這個cookie不指定域,而且對於頁面的客戶端指令碼是不可使用的,因為CookieHttpOnly預設為True。
其他的值可以通過SessionOptions配置:
services.AddSession(options => { options.Cookie.Name = "Test.Session"; options.IdleTimeout = TimeSpan.FromSeconds(10); });
IdleTimeout 在服務端決定過期時間,session的過期時間是獨立於cookie的。
(2)ISession
安裝和配置好session之後,就可以通過HttpContext的一個名為Session,型別為ISession的屬性來引用會話。
public interface ISession { // // 摘要: // Indicate whether the current session has loaded. bool IsAvailable { get; } // // 摘要: // A unique identifier for the current session. This is not the same as the session // cookie since the cookie lifetime may not be the same as the session entry lifetime // in the data store. string Id { get; } // // 摘要: // Enumerates all the keys, if any. IEnumerable<string> Keys { get; } // // 摘要: // Remove all entries from the current session, if any. The session cookie is not // removed. void Clear(); // // 摘要: // Store the session in the data store. This may throw if the data store is unavailable. Task CommitAsync(CancellationToken cancellationToken = default(CancellationToken)); // // 摘要: // Load the session from the data store. This may throw if the data store is unavailable. Task LoadAsync(CancellationToken cancellationToken = default(CancellationToken)); // // 摘要: // Remove the given key from the session if present. // // 引數: // key: void Remove(string key); // // 摘要: // Set the given key and value in the current session. This will throw if the session // was not established prior to sending the response. // // 引數: // key: // // value: void Set(string key, byte[] value); // // 摘要: // Retrieve the value of the given key, if present. // // 引數: // key: // // value: bool TryGetValue(string key, out byte[] value); }
因為Session是建立在IDistributedCache之上的,所以總是需要序列化被儲存的物件例項。因此,這個介面是使用byte[]而不是直接使用object。string 和 int32 的簡單型別可以直接使用:
HttpContext.Session.SetInt32("key",123); HttpContext.Session.GetInt32("key");
儲存物件需要先把物件序列化為一個byte[]位元組流。需要使用MemoryStream 和 BinaryFormatter
/// <summary> /// 將一個object物件序列化,返回一個byte[] /// </summary> /// <param name="obj">能序列化的物件</param> /// <returns></returns> public static byte[] ObjectToBytes(object obj) { using (MemoryStream ms = new MemoryStream()) { IFormatter formatter = new BinaryFormatter(); formatter.Serialize(ms, obj); return ms.GetBuffer(); } } /// <summary> /// 將一個序列化後的byte[]陣列還原 /// </summary> /// <param name="Bytes"></param> /// <returns></returns> public static object BytesToObject(byte[] Bytes) { using (MemoryStream ms = new MemoryStream(Bytes)) { IFormatter formatter = new BinaryFormatter(); return formatter.Deserialize(ms); } }