Session và quản lý trạng thái (State management) trong ASP.NET Core
Bởi Rick Anderson, Kirk Larkin, và Diana LaRose
HTTP là giao thức không trạng thái (stateless protocol). Theo mặc định, các yêu cầu HTTP là các tin nhắn độc lập không lưu giữ giá trị người dùng. Bài viết này mô tả một số phương pháp để lưu trữ dữ liệu người dùng giữa các yêu cầu.
Để biết hướng dẫn quản lý trạng thái Blazor, xem ASP.NET Core Blazor state management overview.
Quản lý trạng thái (State management)
Trạng thái có thể được lưu trữ bằng nhiều phương pháp. Mỗi phương pháp được mô tả ở phần sau trong bài viết này.
| Phương pháp lưu trữ | Cơ chế lưu trữ |
|---|---|
| Cookies | HTTP cookies. Có thể bao gồm dữ liệu được lưu trữ bằng code ứng dụng phía server. |
| Session state (Trạng thái phiên) | HTTP cookies và code ứng dụng phía server |
| TempData | HTTP cookies hoặc session state |
| Query strings (Chuỗi truy vấn) | HTTP query strings |
| Hidden fields (Trường ẩn) | HTTP form fields |
| HttpContext.Items | Code ứng dụng phía server |
| Cache (Bộ nhớ đệm) | Code ứng dụng phía server |
SignalR/Blazor Server và quản lý trạng thái dựa trên HTTP context
Các ứng dụng SignalR không nên sử dụng session state và các phương pháp quản lý trạng thái khác phụ thuộc vào HTTP context ổn định để lưu trữ thông tin. Các ứng dụng SignalR có thể lưu trữ trạng thái theo kết nối trong Context.Items trong hub. Để biết thêm thông tin và các phương pháp quản lý trạng thái thay thế cho các ứng dụng Blazor Server, xem ASP.NET Core Blazor state management overview và ASP.NET Core Blazor server-side state management.
Cookies
Cookies lưu trữ dữ liệu qua các yêu cầu. Vì cookies được gửi kèm mỗi yêu cầu, kích thước của chúng nên được giữ ở mức tối thiểu. Lý tưởng nhất, chỉ nên lưu trữ một định danh trong cookie với dữ liệu được lưu trữ bởi ứng dụng. Hầu hết các trình duyệt giới hạn kích thước cookie ở 4096 bytes. Chỉ có một số lượng cookie giới hạn có sẵn cho mỗi tên miền.
Vì cookies có thể bị giả mạo, chúng phải được xác thực bởi ứng dụng. Cookies có thể bị xóa bởi người dùng và hết hạn trên các client. Tuy nhiên, cookies thường là hình thức lưu trữ dữ liệu bền vững nhất trên client.
Cookies thường được sử dụng để cá nhân hóa, nơi nội dung được tùy chỉnh cho một người dùng đã biết. Người dùng chỉ được xác định chứ không được xác thực trong hầu hết các trường hợp. Cookie có thể lưu trữ tên người dùng, tên tài khoản hoặc ID người dùng duy nhất như GUID. Cookie có thể được sử dụng để truy cập các cài đặt cá nhân của người dùng, chẳng hạn như màu nền trang web ưa thích của họ.
Xem General Data Protection Regulation (GDPR) của Liên minh Châu Âu khi phát hành cookies và xử lý các mối quan tâm về quyền riêng tư. Để biết thêm thông tin, xem General Data Protection Regulation (GDPR) support in ASP.NET Core.
Session state (Trạng thái phiên)
Session state là một kịch bản ASP.NET Core để lưu trữ dữ liệu người dùng trong khi người dùng duyệt một ứng dụng web. Session state sử dụng một kho được duy trì bởi ứng dụng để lưu trữ dữ liệu qua các yêu cầu từ một client. Dữ liệu session được hỗ trợ bởi cache và được coi là dữ liệu tạm thời. Site nên tiếp tục hoạt động mà không có dữ liệu session. Dữ liệu ứng dụng quan trọng nên được lưu trữ trong database người dùng và chỉ được cache trong session như một tối ưu hóa hiệu năng.
Session không được hỗ trợ trong các ứng dụng SignalR vì một SignalR Hub có thể thực thi độc lập với HTTP context. Ví dụ, điều này có thể xảy ra khi một yêu cầu long polling được giữ mở bởi một hub vượt quá vòng đời của HTTP context của yêu cầu.
ASP.NET Core duy trì session state bằng cách cung cấp một cookie cho client chứa session ID. Cookie session ID:
- Được gửi đến ứng dụng với mỗi yêu cầu.
- Được sử dụng bởi ứng dụng để lấy dữ liệu session.
Session state thể hiện các hành vi sau:
- Cookie session dành riêng cho trình duyệt. Các session không được chia sẻ giữa các trình duyệt.
- Cookie session bị xóa khi session trình duyệt kết thúc.
- Nếu một cookie được nhận cho một session đã hết hạn, một session mới được tạo sử dụng cùng cookie session.
- Các session trống không được lưu. Session phải có ít nhất một giá trị được đặt để lưu trữ session qua các yêu cầu. Khi một session không được lưu, một session ID mới được tạo cho mỗi yêu cầu mới.
- Ứng dụng giữ một session trong một khoảng thời gian giới hạn sau yêu cầu cuối cùng. Ứng dụng đặt session timeout hoặc sử dụng giá trị mặc định là 20 phút. Session state lý tưởng để lưu trữ dữ liệu người dùng:
- Đặc thù cho một session cụ thể.
- Nơi dữ liệu không yêu cầu lưu trữ vĩnh viễn qua các session.
- Dữ liệu session bị xóa khi triển khai ISession.Clear được gọi hoặc khi session hết hạn.
- Không có cơ chế mặc định để thông báo cho code ứng dụng rằng trình duyệt client đã bị đóng hoặc khi cookie session bị xóa hoặc hết hạn trên client.
- Cookie session state không được đánh dấu là essential (cần thiết) theo mặc định. Session state không hoạt động trừ khi tracking được cho phép bởi khách truy cập site. Để biết thêm thông tin, xem General Data Protection Regulation (GDPR) support in ASP.NET Core.
- Lưu ý: Không có sự thay thế cho tính năng cookieless session từ ASP.NET Framework vì nó được coi là không an toàn và có thể dẫn đến các cuộc tấn công session fixation.
Cảnh báo: Đừng lưu trữ dữ liệu nhạy cảm trong session state. Người dùng có thể không đóng trình duyệt và xóa cookie session. Một số trình duyệt duy trì các cookie session hợp lệ qua các cửa sổ trình duyệt. Một session có thể không bị giới hạn cho một người dùng duy nhất. Người dùng tiếp theo có thể tiếp tục duyệt ứng dụng với cùng cookie session.
In-memory cache provider (nhà cung cấp cache trong bộ nhớ) lưu trữ dữ liệu session trong bộ nhớ của server nơi ứng dụng đang chạy. Trong một kịch bản server farm (trang trại server):
- Sử dụng sticky sessions để gắn mỗi session với một instance ứng dụng cụ thể trên một server riêng lẻ. Azure App Service sử dụng Application Request Routing (ARR) để thực thi sticky sessions theo mặc định. Tuy nhiên, sticky sessions có thể ảnh hưởng đến khả năng mở rộng và làm phức tạp việc cập nhật ứng dụng web. Phương pháp tốt hơn là sử dụng distributed cache Redis, SQL Server, hoặc Azure Postgres, không yêu cầu sticky sessions. Để biết thêm thông tin, xem Distributed caching in ASP.NET Core.
- Cookie session được mã hóa thông qua IDataProtector. Data Protection phải được cấu hình đúng để đọc cookie session trên mỗi máy. Để biết thêm thông tin, xem ASP.NET Core Data Protection Overview và Key storage providers.
Cấu hình session state
Middleware (phần mềm trung gian) để quản lý session state được bao gồm trong framework. Để bật session middleware, Program.cs phải chứa:
- Bất kỳ cache bộ nhớ IDistributedCache nào. Triển khai
IDistributedCacheđược sử dụng như một backing store (kho lưu trữ nền) cho session. Để biết thêm thông tin, xem Distributed caching in ASP.NET Core. - Một lời gọi đến AddSession
- Một lời gọi đến UseSession
Code sau đây cho thấy cách thiết lập in-memory session provider với triển khai IDistributedCache trong bộ nhớ mặc định:
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();Code trên đặt timeout ngắn để đơn giản hóa việc kiểm tra.
Thứ tự middleware quan trọng. Gọi UseSession sau UseRouting và trước MapRazorPages và MapDefaultControllerRoute. Xem Middleware Ordering.
HttpContext.Session có sẵn sau khi session state được cấu hình.
HttpContext.Session không thể được truy cập trước khi UseSession được gọi.
Một session mới với một cookie session mới không thể được tạo sau khi ứng dụng đã bắt đầu viết vào response stream. Ngoại lệ được ghi lại trong log web server và không được hiển thị trong trình duyệt.
Tải session state bất đồng bộ (Load session state asynchronously)
Session provider mặc định trong ASP.NET Core tải session records từ backing store IDistributedCache bất đồng bộ chỉ khi phương thức ISession.LoadAsync được gọi rõ ràng trước các phương thức TryGetValue, Set, hoặc Remove. Nếu LoadAsync không được gọi trước, session record nền được tải đồng bộ, có thể gây ra hình phạt hiệu năng ở quy mô lớn.
Để các ứng dụng thực thi pattern này, hãy bọc các triển khai DistributedSessionStore và DistributedSession với các phiên bản ném ngoại lệ nếu phương thức LoadAsync không được gọi trước TryGetValue, Set, hoặc Remove. Đăng ký các phiên bản được bọc trong services container.
Tùy chọn Session (Session options)
Để ghi đè các giá trị mặc định của session, sử dụng SessionOptions.
| Tùy chọn | Mô tả |
|---|---|
| Cookie | Xác định các cài đặt được sử dụng để tạo cookie. Name mặc định là SessionDefaults.CookieName (.AspNetCore.Session). Path mặc định là SessionDefaults.CookiePath (/). SameSite mặc định là SameSiteMode.Lax (1). HttpOnly mặc định là true. IsEssential mặc định là false. |
| IdleTimeout | IdleTimeout cho biết session có thể nhàn rỗi trong bao lâu trước khi nội dung của nó bị bỏ. Mỗi lần truy cập session sẽ reset timeout. Cài đặt này chỉ áp dụng cho nội dung của session, không phải cookie. Mặc định là 20 phút. |
| IOTimeout | Lượng thời gian tối đa được phép để tải một session từ store hoặc commit nó trở lại store. Cài đặt này chỉ có thể áp dụng cho các thao tác bất đồng bộ. Timeout này có thể bị vô hiệu hóa bằng cách sử dụng InfiniteTimeSpan. Mặc định là 1 phút. |
Session sử dụng cookie để theo dõi và xác định các yêu cầu từ một trình duyệt duy nhất. Theo mặc định, cookie này có tên .AspNetCore.Session, và sử dụng path /. Vì cookie mặc định không chỉ định domain, nó không được cung cấp cho script phía client trên trang (vì HttpOnly mặc định là true).
Để ghi đè các giá trị mặc định của cookie session, sử dụng 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();Ứng dụng sử dụng thuộc tính IdleTimeout để xác định session có thể nhàn rỗi bao lâu trước khi nội dung của nó trong cache server bị bỏ. Thuộc tính này độc lập với thời hạn cookie. Mỗi yêu cầu đi qua Session Middleware sẽ reset timeout.
Session state là non-locking (không khóa). Nếu hai yêu cầu đồng thời cố gắng sửa đổi nội dung của một session, yêu cầu cuối cùng ghi đè yêu cầu đầu tiên. Session được triển khai như một coherent session (session nhất quán), có nghĩa là tất cả nội dung được lưu trữ cùng nhau. Khi hai yêu cầu tìm cách sửa đổi các giá trị session khác nhau, yêu cầu cuối cùng có thể ghi đè các thay đổi session được thực hiện bởi yêu cầu đầu tiên.
Đặt và lấy giá trị Session
Session state được truy cập từ lớp Razor Pages PageModel hoặc lớp MVC Controller với HttpContext.Session. Thuộc tính này là một triển khai ISession.
Triển khai ISession cung cấp một số extension methods để đặt và truy xuất giá trị integer và string. Các extension methods nằm trong namespace Microsoft.AspNetCore.Http.
Extension methods của ISession:
- Get(ISession, String)
- GetInt32(ISession, String)
- GetString(ISession, String)
- SetInt32(ISession, String, Int32)
- SetString(ISession, String, String)
Ví dụ sau lấy giá trị session cho key IndexModel.SessionKeyName (_Name trong ứng dụng mẫu) trong một trang Razor Pages:
@page @using Microsoft.AspNetCore.Http @model IndexModel ... Name: @HttpContext.Session.GetString(IndexModel.SessionKeyName)
Ví dụ sau cho thấy cách đặt và lấy một integer và một string:
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);
}
}Tất cả dữ liệu session phải được serialize để cho phép kịch bản distributed cache, ngay cả khi sử dụng in-memory cache. Các serializer string và integer được cung cấp bởi các extension methods của ISession. Các kiểu phức tạp phải được serialize bởi người dùng bằng cơ chế khác, chẳng hạn như JSON.
Sử dụng code mẫu sau để serialize (tuần tự hóa) các object:
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);
}
}Ví dụ sau cho thấy cách đặt và lấy một object có thể serialize với lớp 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));
}
}
}Cảnh báo: Lưu trữ một object trực tiếp trong session nên được sử dụng cẩn thận, vì có nhiều vấn đề có thể xảy ra với các object được serialize. Để biết thêm thông tin, xem Sessions should be allowed to store objects (dotnet/aspnetcore #18159).
TempData
ASP.NET Core cung cấp Razor Pages TempData hoặc Controller TempData. Thuộc tính này lưu trữ dữ liệu cho đến khi nó được đọc trong một yêu cầu khác. Các phương thức Keep(String) và Peek(string) có thể được sử dụng để kiểm tra dữ liệu mà không xóa ở cuối yêu cầu. Keep đánh dấu tất cả các mục trong dictionary để giữ lại. TempData:
- Hữu ích cho chuyển hướng khi dữ liệu cần thiết cho nhiều hơn một yêu cầu.
- Được triển khai bởi các
TempDataproviders sử dụng cookies hoặc session state.
Ví dụ về TempData
Xem xét trang sau tạo một 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");
}
}Trang sau hiển thị TempData["Message"]:
@page
@model IndexModel
<h1>Peek Contacts</h1>
@{
if (TempData.Peek("Message") != null)
{
<h3>Message: @TempData.Peek("Message")</h3>
}
}Trong markup trên, ở cuối yêu cầu, TempData["Message"] không bị xóa vì Peek được sử dụng. Làm mới trang hiển thị nội dung của TempData["Message"].
Markup sau tương tự như code trên, nhưng sử dụng Keep để giữ lại dữ liệu ở cuối yêu cầu:
@page
@model IndexModel
<h1>Contacts Keep</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
TempData.Keep("Message");
}Điều hướng giữa các trang IndexPeek và IndexKeep sẽ không xóa TempData["Message"].
Code sau hiển thị TempData["Message"], nhưng ở cuối yêu cầu, TempData["Message"] bị xóa:
@page
@model IndexModel
<h1>Index no Keep or Peek</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
}Các TempData providers
Cookie-based TempData provider (nhà cung cấp TempData dựa trên cookie) được sử dụng theo mặc định để lưu trữ TempData trong cookies.
Dữ liệu cookie được mã hóa bằng IDataProtector, được mã hóa với Base64UrlTextEncoder, sau đó được chia nhỏ. Kích thước cookie tối đa nhỏ hơn 4096 bytes do mã hóa và chia nhỏ. Dữ liệu cookie không được nén vì nén dữ liệu đã mã hóa có thể dẫn đến các vấn đề bảo mật như các cuộc tấn công CRIME và BREACH.
Chọn TempData provider
Việc chọn TempData provider liên quan đến một số cân nhắc, chẳng hạn như:
- Ứng dụng đã sử dụng session state chưa? Nếu có, sử dụng session state TempData provider không có chi phí bổ sung cho ứng dụng ngoài kích thước của dữ liệu.
- Ứng dụng có sử dụng TempData một cách tiết kiệm cho lượng dữ liệu tương đối nhỏ, tối đa 500 bytes không? Nếu có, cookie TempData provider thêm một chi phí nhỏ cho mỗi yêu cầu mang TempData. Nếu không, session state TempData provider có thể có lợi để tránh round-tripping một lượng lớn dữ liệu trong mỗi yêu cầu cho đến khi TempData được sử dụng.
- Ứng dụng có chạy trong một server farm trên nhiều server không? Nếu có, không cần cấu hình bổ sung để sử dụng cookie TempData provider ngoài Data Protection.
Hầu hết các web client như trình duyệt web thực thi giới hạn về kích thước tối đa của mỗi cookie và tổng số cookie. Khi sử dụng cookie TempData provider, hãy xác minh ứng dụng sẽ không vượt quá các giới hạn này. Xem xét tổng kích thước của dữ liệu. Tính đến sự tăng kích thước cookie do mã hóa và chia nhỏ.
Cấu hình TempData provider
Cookie-based TempData provider được bật theo mặc định.
Để bật session-based TempData provider, sử dụng extension method AddSessionStateTempDataProvider. Chỉ cần một lời gọi đến 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();Query strings (Chuỗi truy vấn)
Một lượng dữ liệu giới hạn có thể được truyền từ một yêu cầu sang yêu cầu khác bằng cách thêm nó vào query string của yêu cầu mới. Điều này hữu ích để lưu trạng thái theo cách bền vững cho phép các liên kết có trạng thái nhúng được chia sẻ qua email hoặc mạng xã hội. Vì URL query strings là công khai, đừng bao giờ sử dụng query strings cho dữ liệu nhạy cảm.
Ngoài việc chia sẻ không có chủ ý, việc đưa dữ liệu vào query strings có thể làm lộ ứng dụng cho các cuộc tấn công Cross-Site Request Forgery (CSRF). Bất kỳ session state được lưu nào cũng phải bảo vệ chống lại các cuộc tấn công CSRF.
Hidden fields (Trường ẩn)
Dữ liệu có thể được lưu trong các trường form ẩn và đăng trở lại trong yêu cầu tiếp theo. Điều này phổ biến trong các form nhiều trang. Vì client có thể giả mạo dữ liệu, ứng dụng phải luôn xác thực lại dữ liệu được lưu trữ trong các hidden fields.
HttpContext.Items
Collection HttpContext.Items được sử dụng để lưu trữ dữ liệu trong khi xử lý một yêu cầu duy nhất. Nội dung của collection bị hủy sau khi một yêu cầu được xử lý. Collection Items thường được sử dụng để cho phép các components hoặc middleware giao tiếp khi chúng hoạt động ở các thời điểm khác nhau trong một yêu cầu và không có cách nào trực tiếp để truyền tham số.
Trong ví dụ sau, middleware thêm isVerified vào collection 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();Đối với middleware chỉ được sử dụng trong một ứng dụng duy nhất, việc sử dụng key string cố định khó gây ra xung đột key. Tuy nhiên, để tránh hoàn toàn khả năng xung đột key, một object có thể được sử dụng làm item key. Phương pháp này đặc biệt hữu ích cho middleware được chia sẻ giữa các ứng dụng và cũng có lợi thế là loại bỏ việc sử dụng các key string trong code. Ví dụ sau cho thấy cách sử dụng key object được định nghĩa trong một lớp 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>();
}
}Code khác có thể truy cập giá trị được lưu trong HttpContext.Items bằng cách sử dụng key được cung cấp bởi lớp 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 (Bộ nhớ đệm)
Caching là một cách hiệu quả để lưu trữ và truy xuất dữ liệu. Ứng dụng có thể kiểm soát thời gian tồn tại của các mục được cache. Để biết thêm thông tin, xem Response caching in ASP.NET Core.
Dữ liệu được cache không liên quan đến một yêu cầu, người dùng hoặc session cụ thể. Không cache dữ liệu dành riêng cho người dùng có thể được truy xuất bởi các yêu cầu người dùng khác.
Để cache dữ liệu toàn ứng dụng, xem Cache in-memory in ASP.NET Core.
Kiểm tra session state
ISession.IsAvailable nhằm mục đích kiểm tra các lỗi tạm thời. Gọi IsAvailable trước khi session middleware chạy sẽ ném ra InvalidOperationException.
Các thư viện cần kiểm tra tính khả dụng của session có thể sử dụng HttpContext.Features.Get<ISessionFeature>()?.Session != null.
Các lỗi thường gặp
- "Unable to resolve service for type 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' while attempting to activate 'Microsoft.AspNetCore.Session.DistributedSessionStore'."
Điều này thường do không cấu hình ít nhất một triển khai IDistributedCache. Để biết thêm thông tin, xem Distributed caching in ASP.NET Core và Cache in-memory in ASP.NET Core.
Nếu session middleware không lưu được một session:
- Middleware ghi log ngoại lệ và yêu cầu tiếp tục bình thường.
- Điều này dẫn đến hành vi không thể đoán trước.
Session middleware có thể thất bại trong việc lưu một session nếu backing store không khả dụng. Ví dụ, một người dùng lưu trữ giỏ hàng trong session. Người dùng thêm một mặt hàng vào giỏ nhưng commit thất bại. Ứng dụng không biết về sự thất bại nên báo cáo cho người dùng rằng mặt hàng đã được thêm vào giỏ, điều này là không đúng.
Cách tiếp cận được khuyến nghị để kiểm tra lỗi là gọi await feature.Session.CommitAsync khi ứng dụng hoàn thành việc ghi vào session. CommitAsync ném ngoại lệ nếu backing store không khả dụng. Nếu CommitAsync thất bại, ứng dụng có thể xử lý ngoại lệ. LoadAsync ném dưới các điều kiện tương tự khi data store không khả dụng.