Response Caching Middleware (Phần mềm trung gian Cache phản hồi) trong ASP.NET Core
Bởi John Luo và Rick Anderson
Bài viết này giải thích cách cấu hình Response Caching Middleware trong ứng dụng ASP.NET Core. Middleware xác định khi nào response (phản hồi) có thể được cache (lưu vào bộ nhớ đệm), lưu trữ response, và phục vụ response từ cache. Để giới thiệu về HTTP caching và attribute [ResponseCache], xem Response Caching.
Response caching middleware (phần mềm trung gian cache phản hồi) cho phép cache response của server dựa trên HTTP Cache-Control headers.
- Hành vi caching thực hiện ngữ nghĩa HTTP caching chuẩn.
- Caching dựa trên HTTP cache headers tương tự phương pháp được dùng bởi proxy.
- Hình thức caching này hữu ích cho các public GET hoặc HEAD API request từ client khi điều kiện để cache được đáp ứng.
- Đối với ứng dụng UI như Razor Pages, response caching thường không có lợi. Các trình duyệt thường đặt request headers ngăn caching.
Output caching (có trong .NET 7 trở lên) là phương pháp tốt hơn cho ứng dụng UI. Trong kịch bản đó, cấu hình xác định những gì cần cache độc lập với HTTP headers.
Để kiểm thử response caching, dùng Fiddler hoặc công cụ khác có thể đặt request headers một cách rõ ràng. Việc đặt headers rõ ràng là ưu tiên cho việc kiểm thử caching.
Cấu hình
Trong Program.cs, thêm Response Caching Middleware services AddResponseCaching vào service collection và cấu hình ứng dụng sử dụng middleware với extension method UseResponseCaching. UseResponseCaching thêm middleware vào request processing pipeline:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddResponseCaching(); var app = builder.Build(); app.UseHttpsRedirection(); // UseCors phải được gọi trước UseResponseCaching //app.UseCors(); app.UseResponseCaching();
Cảnh báo: UseCors phải được gọi trước UseResponseCaching khi dùng CORS middleware.
Ứng dụng mẫu thêm headers để kiểm soát caching trên các request tiếp theo:
Cache-Control: Cache các response có thể cache trong tối đa 10 giây.Vary: Cấu hình middleware phục vụ cached response chỉ khi headerAccept-Encodingcủa các request tiếp theo khớp với request gốc.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddResponseCaching();
var app = builder.Build();
app.UseHttpsRedirection();
// UseCors phải được gọi trước UseResponseCaching
//app.UseCors();
app.UseResponseCaching();
app.Use(async (context, next) =>
{
context.Response.GetTypedHeaders().CacheControl =
new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
new string[] { "Accept-Encoding" };
await next();
});
app.MapGet("/", () => DateTime.Now.Millisecond);
app.Run();Các headers trên không được ghi vào response và bị ghi đè khi controller, action, hoặc Razor Page:
- Có attribute
[ResponseCache]. Điều này áp dụng ngay cả khi property không được đặt. Ví dụ, bỏ qua propertyVaryByHeadersẽ khiến header tương ứng bị xóa khỏi response.
Response Caching Middleware chỉ cache response của server với status code 200 (OK). Các response khác, bao gồm error pages (trang lỗi), đều bị middleware bỏ qua.
Cảnh báo: Các response chứa nội dung cho client đã xác thực phải được đánh dấu là không thể cache để ngăn middleware lưu trữ và phục vụ những response đó. Xem phần Điều kiện để cache để biết chi tiết về cách middleware xác định response có thể được cache hay không.
Tùy chọn
Các tùy chọn response caching được hiển thị trong bảng sau.
| Tùy chọn | Mô tả |
|---|---|
MaximumBodySize | Kích thước lớn nhất có thể cache của response body (nội dung phản hồi) tính bằng byte. Giá trị mặc định là 64 * 1024 * 1024 (64 MB). |
SizeLimit | Giới hạn kích thước cho response cache middleware tính bằng byte. Giá trị mặc định là 100 * 1024 * 1024 (100 MB). |
UseCaseSensitivePaths | Xác định xem response có được cache trên các path (đường dẫn) phân biệt chữ hoa/thường hay không. Giá trị mặc định là false. |
Ví dụ sau cấu hình middleware để:
- Cache các response với body size nhỏ hơn hoặc bằng 1.024 bytes.
- Lưu trữ các response theo path phân biệt chữ hoa/thường. Ví dụ,
/page1và/Page1được lưu riêng biệt.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddResponseCaching(options =>
{
options.MaximumBodySize = 1024;
options.UseCaseSensitivePaths = true;
});
var app = builder.Build();
app.UseHttpsRedirection();
// UseCors phải được gọi trước UseResponseCaching
//app.UseCors();
app.UseResponseCaching();
app.Use(async (context, next) =>
{
context.Response.GetTypedHeaders().CacheControl =
new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
new string[] { "Accept-Encoding" };
await next(context);
});
app.MapGet("/", () => DateTime.Now.Millisecond);
app.Run();VaryByQueryKeys
Khi dùng MVC, web API controllers, hoặc Razor Pages page models, attribute [ResponseCache] chỉ định các tham số cần thiết để đặt headers thích hợp cho response caching. Tham số duy nhất của attribute [ResponseCache] thực sự yêu cầu middleware là VaryByQueryKeys, không tương ứng với HTTP header thực tế.
Khi không dùng attribute [ResponseCache], response caching có thể biến đổi với VaryByQueryKeys. Dùng ResponseCachingFeature trực tiếp từ HttpContext.Features:
var responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>();
if (responseCachingFeature != null)
{
responseCachingFeature.VaryByQueryKeys = new[] { "MyKey" };
}Dùng giá trị duy nhất * trong VaryByQueryKeys sẽ biến đổi cache theo tất cả các query parameter (tham số truy vấn) của request.
HTTP headers được dùng bởi Response Caching Middleware
Bảng sau cung cấp thông tin về HTTP headers ảnh hưởng đến response caching.
| Header | Chi tiết |
|---|---|
Authorization | Response không được cache nếu header này tồn tại. |
Cache-Control | Middleware chỉ xem xét cache các response được đánh dấu với cache directive public. Kiểm soát caching với các tham số: max-age, max-stale†, min-fresh, must-revalidate, no-cache, no-store, only-if-cached, private, public, s-maxage, proxy-revalidate‡. †Nếu không có giới hạn nào được chỉ định cho max-stale, middleware không thực hiện hành động nào. ‡proxy-revalidate có tác dụng giống must-revalidate. |
Pragma | Header Pragma: no-cache trong request tạo ra hiệu ứng tương tự Cache-Control: no-cache. Header này bị ghi đè bởi các directive liên quan trong header Cache-Control nếu có. Được xem xét để tương thích ngược với HTTP/1.0. |
Set-Cookie | Response không được cache nếu header này tồn tại. Bất kỳ middleware nào trong pipeline xử lý request đặt một hoặc nhiều cookie sẽ ngăn Response Caching Middleware cache response (ví dụ, cookie-based TempData provider). |
Vary | Header Vary được dùng để biến đổi cached response theo header khác. Ví dụ, cache response theo encoding bằng cách thêm header Vary: Accept-Encoding, cache response cho các request có headers Accept-Encoding: gzip và Accept-Encoding: text/plain riêng biệt. Response với giá trị header * không bao giờ được lưu. |
Expires | Response được coi là cũ bởi header này sẽ không được lưu hoặc truy xuất trừ khi bị ghi đè bởi các header Cache-Control khác. |
If-None-Match | Response đầy đủ được phục vụ từ cache nếu giá trị không phải * và ETag của response không khớp với bất kỳ giá trị nào được cung cấp. Nếu không, một response 304 (Not Modified) được phục vụ. |
If-Modified-Since | Nếu header If-None-Match không có, response đầy đủ được phục vụ từ cache nếu ngày cached response mới hơn giá trị được cung cấp. Nếu không, response 304 - Not Modified được phục vụ. |
Date | Khi phục vụ từ cache, header Date được đặt bởi middleware nếu nó không được cung cấp trong response gốc. |
Content-Length | Khi phục vụ từ cache, header Content-Length được đặt bởi middleware nếu nó không được cung cấp trong response gốc. |
Age | Header Age được gửi trong response gốc bị bỏ qua. Middleware tính toán giá trị mới khi phục vụ cached response. |
Caching tuân theo Cache-Control directives của request
Middleware tuân theo các quy tắc của RFC 9111: HTTP Caching (Section 5.2. Cache-Control). Các quy tắc yêu cầu cache phải tôn trọng header Cache-Control hợp lệ được gửi bởi client. Theo specification (đặc tả), client có thể thực hiện request với giá trị header no-cache và buộc server tạo response mới cho mỗi request. Hiện tại, không có developer control (kiểm soát của nhà phát triển) nào đối với hành vi caching này khi sử dụng middleware vì middleware tuân theo specification caching chính thức.
Để kiểm soát hành vi caching nhiều hơn, hãy khám phá các tính năng caching khác của ASP.NET Core:
- Cache in-memory trong ASP.NET Core
- Distributed caching trong ASP.NET Core
- Cache Tag Helper trong ASP.NET Core MVC
- Distributed Cache Tag Helper trong ASP.NET Core
Xử lý sự cố
Response Caching Middleware dùng IMemoryCache, có dung lượng giới hạn. Khi dung lượng bị vượt quá, memory cache sẽ được compact (nén).
Nếu hành vi caching không như mong đợi, hãy xác nhận rằng các response có thể được cache và có khả năng được phục vụ từ cache. Kiểm tra các incoming headers của request và outgoing headers của response. Bật logging (ghi nhật ký) để hỗ trợ debug.
Khi kiểm thử và xử lý sự cố hành vi caching, trình duyệt thường đặt request headers ngăn caching. Ví dụ, trình duyệt có thể đặt header Cache-Control thành no-cache hoặc max-age=0 khi làm mới trang. Fiddler và các công cụ khác có thể đặt request headers một cách rõ ràng và được ưu tiên để kiểm thử caching.
Điều kiện để cache
- Request phải dẫn đến response của server với status code 200 (OK).
- Request method phải là GET hoặc HEAD.
- Response Caching Middleware phải được đặt trước middleware yêu cầu caching. Xem thêm ASP.NET Core Middleware.
- Header
Authorizationkhông được có. - Tham số header
Cache-Controlphải hợp lệ, và response phải được đánh dấupublicvà không đánh dấuprivate. - Header
Pragma: no-cachekhông được có nếu headerCache-Controlkhông có, vì headerCache-Controlghi đè headerPragmakhi có. - Header
Set-Cookiekhông được có. - Tham số header
Varyphải hợp lệ và không bằng*. - Giá trị header
Content-Length(nếu được đặt) phải khớp với kích thước của response body. IHttpSendFileFeaturekhông được sử dụng.- Response không được cũ theo quy định của header
Expiresvà các cache directivemax-agevàs-maxage. - Response buffering phải thành công. Kích thước của response phải nhỏ hơn
SizeLimitđã cấu hình hoặc mặc định. Body size của response phải nhỏ hơnMaximumBodySizeđã cấu hình hoặc mặc định. - Response phải có thể cache theo RFC 9111: HTTP Caching. Ví dụ, directive
no-storekhông được có trong request hoặc response header fields.
Lưu ý: Hệ thống Antiforgery (chống giả mạo) để tạo secure tokens (token bảo mật) ngăn Cross-Site Request Forgery (CSRF) tấn công đặt các header Cache-Control và Pragma thành no-cache để các response không được cache.