UZMAN
ASP.NET Output Cache & Response Cache
HTTP response'ları Redis'te cache'leyerek backend yükünü dramatik azaltma.
Kod örneği görünümü
Bu sayfadaki eşleşen örnekleri seçilen istemciye göre gösterir.
Ne Zaman Output Cache Kullan / Kullanma
| Kullan | Kullanma | Gerçek Hayat |
|---|---|---|
| Aynı response farklı kullanıcılara dönüyor (public) | Her kullanıcıya farklı response (kişiselleştirilmiş) | E-ticaret: Ürün listesi sayfası — tüm ziyaretçiler aynı listeyi görür |
| Response üretmek pahalı (DB query + serialization) | Response çok küçük ve hızlı üretiliyor (<1ms) | CMS: Blog post render — Markdown→HTML 50ms, cache'ten 0ms |
| Read-heavy endpoint (%90+ GET) | Sık değişen veri (anlık stok, canlı skor) | API: /api/categories — günde 1 kez değişir, saatte 100K hit |
| Multi-instance uygulama (shared cache gerekli) | Tek pod + IMemoryCache yeterli | SaaS: 10 pod arkasında — Output Cache Redis'te → hepsi aynı cache |
Gerçek hayat senaryosu — Blog platformu: Her blog post'u render etmek 40-80ms (Markdown parse + syntax highlight + template). Output Cache 5dk TTL → ilk request sonrası tüm ziyaretçiler 0ms'de response alır. Post güncellenince
IOutputCacheStore.EvictByTagAsync("post:123")ile invalidate.
IDistributedCache ile Response Caching
# ASP.NET otomatik key oluşturur:
GET "myapp:ResponseCache:/api/products?page=1"
TTL "myapp:ResponseCache:/api/products?page=1"
// === Output Caching (.NET 7+) — Redis backend ===
// NuGet: dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
builder.Services.AddStackExchangeRedisOutputCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("Redis");
options.InstanceName = "oc:";
});
builder.Services.AddOutputCache(options =>
{
// Global policy
options.AddBasePolicy(builder => builder.Expire(TimeSpan.FromMinutes(5)));
// Named policy
options.AddPolicy("ProductList", builder =>
builder.Expire(TimeSpan.FromMinutes(10))
.Tag("products"));
// Vary by query string
options.AddPolicy("Paginated", builder =>
builder.SetVaryByQuery("page", "size")
.Expire(TimeSpan.FromMinutes(5)));
});
var app = builder.Build();
app.UseOutputCache();
// Endpoint'lere uygula
app.MapGet("/api/products", async (AppDbContext db) =>
{
var products = await db.Products.AsNoTracking().ToListAsync();
return Results.Ok(products);
})
.CacheOutput("ProductList");
// Tag-based invalidation
app.MapPost("/api/products", async (
Product product,
AppDbContext db,
IOutputCacheStore cache) =>
{
db.Products.Add(product);
await db.SaveChangesAsync();
// "products" tag'li tüm cache entry'leri temizle
await cache.EvictByTagAsync("products", default);
return Results.Created($"/api/products/{product.Id}", product);
});
// Controller'da kullanım
[OutputCache(Duration = 300, VaryByQueryKeys = new[] { "category" })]
[HttpGet]
public async Task<IActionResult> GetProducts([FromQuery] string? category)
{
// ...
}
// === Response Caching (HTTP cache headers) ===
builder.Services.AddResponseCaching();
app.UseResponseCaching();
app.MapGet("/api/config", () =>
{
return Results.Ok(new { version = "1.0" });
})
.WithMetadata(new ResponseCacheAttribute
{
Duration = 600,
Location = ResponseCacheLocation.Any,
VaryByQueryKeys = new[] { "env" }
});
| Özellik | Output Cache | Response Cache |
|---|---|---|
| Depolama | Redis (server-side) | HTTP headers (client/proxy) |
| Tag invalidation | Evet | Hayır |
| Programmatic eviction | Evet | Hayır |
| Authenticated | Varsayılan hayır (config ile evet) | Hayır |
| Ne zaman | API response'lar | Statik/public content |