ASP.NET Core'da çıkış önbelleğe alma ara yazılımı

Tarafından Tom Dykstra

Not

Bu, bu makalenin en son sürümü değildir. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.

Önemli

Bu bilgiler, ticari olarak piyasaya sürülmeden önce önemli ölçüde değiştirilebilen bir yayın öncesi ürünle ilgilidir. Burada verilen bilgilerle ilgili olarak Microsoft açık veya zımni hiçbir garanti vermez.

Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.

Bu makalede, ASP.NET Core uygulamasında çıkış önbelleğe alma ara yazılımının nasıl yapılandırılması açıklanır. Çıkış önbelleğine giriş için bkz . Çıkış önbelleğe alma.

Çıktı önbelleğe alma ara yazılımı her tür ASP.NET Core uygulamasında kullanılabilir: Minimal API, Denetleyiciler, MVC ve Razor Sayfalar içeren Web API'leri. Örnek uygulama minimal bir API'dir, ancak göstermiş olduğu her önbelleğe alma özelliği diğer uygulama türlerinde de desteklenir.

Ara yazılımı uygulamaya ekleme

çağrısı AddOutputCacheyaparak çıkış önbelleğe alma ara yazılımını hizmet koleksiyonuna ekleyin.

ara yazılımı çağrısı UseOutputCacheyaparak istek işleme işlem hattına ekleyin.

Not

  • CORS ara yazılımı kullanan uygulamalarda, UseOutputCache sonrasında UseCorsçağrılmalıdır.
  • Razor Pages uygulamaları ve denetleyicili uygulamalar bölümünden UseOutputCache sonra UseRoutingçağrılmalıdır.
  • Çağrısı AddOutputCacheyapar ve UseOutputCache önbelleğe alma davranışını başlatmaz, önbelleğe alma özelliğini kullanılabilir hale getirir. Önbelleğe Alma yanıt verileri aşağıdaki bölümlerde gösterildiği gibi yapılandırılmalıdır.

Bir uç nokta veya sayfa yapılandırma

En düşük API uygulamaları için, aşağıdaki örneklerde gösterildiği gibi öğesini çağırarak CacheOutputveya özniteliğini uygulayarak [OutputCache] önbelleğe alma yapacak bir uç nokta yapılandırın:

app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
app.MapGet("/attribute", [OutputCache] (context) => 
    Gravatar.WriteGravatar(context));

Denetleyicili uygulamalar için özniteliğini [OutputCache] eylem yöntemine uygulayın. Pages uygulamaları için Razor özniteliğini sayfa sınıfına Razor uygulayın.

Birden çok uç nokta veya sayfa yapılandırma

Birden çok uç noktaya uygulanan önbelleğe alma yapılandırmasını belirtmek için ararken AddOutputCache ilkeler oluşturun. Belirli uç noktalar için bir ilke seçilebilirken, temel ilke bir uç nokta koleksiyonu için varsayılan önbelleğe alma yapılandırması sağlar.

Aşağıdaki vurgulanmış kod, uygulamanın tüm uç noktaları için önbelleğe alma işlemini 10 saniyelik süreyle yapılandırıyor. Süre sonu belirtilmezse, varsayılan olarak bir dakika olur.

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => 
        builder.Expire(TimeSpan.FromSeconds(10)));
    options.AddPolicy("Expire20", builder => 
        builder.Expire(TimeSpan.FromSeconds(20)));
    options.AddPolicy("Expire30", builder => 
        builder.Expire(TimeSpan.FromSeconds(30)));
});

Aşağıdaki vurgulanmış kod, her birinde farklı bir süre sonu belirten iki ilke oluşturur. Seçilen uç noktalar 20 saniyelik süre sonunu, diğerleri ise 30 saniyelik süre sonunu kullanabilir.

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => 
        builder.Expire(TimeSpan.FromSeconds(10)));
    options.AddPolicy("Expire20", builder => 
        builder.Expire(TimeSpan.FromSeconds(20)));
    options.AddPolicy("Expire30", builder => 
        builder.Expire(TimeSpan.FromSeconds(30)));
});

Yöntemini çağırırken CacheOutput veya özniteliğini kullanarak [OutputCache] uç nokta için bir ilke seçebilirsiniz:

app.MapGet("/20", Gravatar.WriteGravatar).CacheOutput("Expire20");
app.MapGet("/30", [OutputCache(PolicyName = "Expire30")] (context) => 
    Gravatar.WriteGravatar(context));

Denetleyicili uygulamalar için özniteliğini [OutputCache] eylem yöntemine uygulayın. Pages uygulamaları için Razor özniteliğini sayfa sınıfına Razor uygulayın.

Varsayılan çıkış önbelleğe alma ilkesi

Varsayılan olarak, çıktı önbelleğe alma şu kuralları izler:

  • Yalnızca HTTP 200 yanıtları önbelleğe alınır.
  • Yalnızca HTTP GET veya HEAD istekleri önbelleğe alınır.
  • Ayarlanan cookieyanıtlar önbelleğe alınmaz.
  • Kimliği doğrulanmış isteklere verilen yanıtlar önbelleğe alınmaz.

Aşağıdaki kod, varsayılan önbelleğe alma kurallarının tümünü uygulamanın tüm uç noktalarına uygular:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => builder.Cache());
});

Varsayılan ilkeyi geçersiz kılma

Aşağıdaki kod, varsayılan kuralların nasıl geçersiz kılınacaklarını gösterir. Aşağıdaki özel ilke kodunda vurgulanan satırlar, HTTP POST yöntemleri ve HTTP 301 yanıtları için önbelleğe almayı etkinleştirir:

using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.Primitives;

namespace OCMinimal;

public sealed class MyCustomPolicy : IOutputCachePolicy
{
    public static readonly MyCustomPolicy Instance = new();

    private MyCustomPolicy()
    {
    }

    ValueTask IOutputCachePolicy.CacheRequestAsync(
        OutputCacheContext context, 
        CancellationToken cancellationToken)
    {
        var attemptOutputCaching = AttemptOutputCaching(context);
        context.EnableOutputCaching = true;
        context.AllowCacheLookup = attemptOutputCaching;
        context.AllowCacheStorage = attemptOutputCaching;
        context.AllowLocking = true;

        // Vary by any query by default
        context.CacheVaryByRules.QueryKeys = "*";

        return ValueTask.CompletedTask;
    }

    ValueTask IOutputCachePolicy.ServeFromCacheAsync
        (OutputCacheContext context, CancellationToken cancellationToken)
    {
        return ValueTask.CompletedTask;
    }

    ValueTask IOutputCachePolicy.ServeResponseAsync
        (OutputCacheContext context, CancellationToken cancellationToken)
    {
        var response = context.HttpContext.Response;

        // Verify existence of cookie headers
        if (!StringValues.IsNullOrEmpty(response.Headers.SetCookie))
        {
            context.AllowCacheStorage = false;
            return ValueTask.CompletedTask;
        }

        // Check response code
        if (response.StatusCode != StatusCodes.Status200OK && 
            response.StatusCode != StatusCodes.Status301MovedPermanently)
        {
            context.AllowCacheStorage = false;
            return ValueTask.CompletedTask;
        }

        return ValueTask.CompletedTask;
    }

    private static bool AttemptOutputCaching(OutputCacheContext context)
    {
        // Check if the current request fulfills the requirements
        // to be cached
        var request = context.HttpContext.Request;

        // Verify the method
        if (!HttpMethods.IsGet(request.Method) && 
            !HttpMethods.IsHead(request.Method) && 
            !HttpMethods.IsPost(request.Method))
        {
            return false;
        }

        // Verify existence of authorization headers
        if (!StringValues.IsNullOrEmpty(request.Headers.Authorization) || 
            request.HttpContext.User?.Identity?.IsAuthenticated == true)
        {
            return false;
        }

        return true;
    }
}

Bu özel ilkeyi kullanmak için adlandırılmış bir ilke oluşturun:

builder.Services.AddOutputCache(options =>
{
    options.AddPolicy("CachePost", MyCustomPolicy.Instance);
});

Bir uç nokta için adlandırılmış ilkeyi seçin:

app.MapPost("/cachedpost", Gravatar.WriteGravatar)
    .CacheOutput("CachePost");

Alternatif varsayılan ilke geçersiz kılma

Alternatif olarak, özel ilke sınıfında aşağıdaki değişikliklerle bir örneği başlatmak için Bağımlılık Ekleme (DI) kullanın:

  • Özel oluşturucu yerine ortak oluşturucu.
  • Instance Özel ilke sınıfında özelliğini ortadan kaldırın.

Örneğin:

public sealed class MyCustomPolicy2 : IOutputCachePolicy
{

    public MyCustomPolicy2()
    {
    }

Sınıfın geri kalanı daha önce gösterildiği gibi aynıdır. Aşağıdaki örnekte gösterildiği gibi özel ilkeyi ekleyin:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => 
        builder.AddPolicy<MyCustomPolicy2>(), true);
});

Yukarıdaki kod, özel ilke sınıfının örneğini oluşturmak için DI kullanır. Oluşturucudaki tüm genel bağımsız değişkenler çözümlenir.

Özel ilkeyi temel ilke olarak kullanırken, temel ilkenin geçerli olması gereken uç noktalarda (bağımsız değişken olmadan) çağırmayın OutputCache() . Çağırma OutputCache() , varsayılan ilkeyi uç noktaya ekler.

Önbellek anahtarını belirtme

Varsayılan olarak, URL'nin her bölümü bir önbellek girdisinin anahtarı olarak (şema, konak, bağlantı noktası, yol ve sorgu dizesi) eklenir. Ancak, önbellek anahtarını açıkça denetlemek isteyebilirsiniz. Örneğin, yalnızca sorgu dizesinin her benzersiz değeri culture için benzersiz bir yanıt döndüren bir uç noktanız olduğunu varsayalım. URL'nin diğer bölümlerindeki varyasyon (diğer sorgu dizeleri gibi) farklı önbellek girdilerine neden olmamalıdır. Aşağıdaki vurgulanmış kodda gösterildiği gibi bir ilkede bu tür kuralları belirtebilirsiniz:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => builder
        .With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
        .Tag("tag-blog"));
    options.AddBasePolicy(builder => builder.Tag("tag-all"));
    options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
    options.AddPolicy("NoCache", builder => builder.NoCache());
    options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});

Ardından bir uç nokta için ilkeyi VaryByQuery seçebilirsiniz:

app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput("Query");

Önbellek anahtarını denetleme seçeneklerinden bazıları şunlardır:

  • SetVaryByQuery - Önbellek anahtarına eklenecek bir veya daha fazla sorgu dizesi adı belirtin.

  • SetVaryByHeader - Önbellek anahtarına eklenecek bir veya daha fazla HTTP üst bilgisi belirtin.

  • VaryByValue- Önbellek anahtarına eklenecek değeri belirtin. Aşağıdaki örnek, geçerli sunucu saatinin saniye olarak tek mi yoksa çift mi olduğunu gösteren bir değer kullanır. Yeni yanıt yalnızca saniye sayısı tekten çifte, hatta teke geçtiğinde oluşturulur.

    app.MapGet("/varybyvalue", Gravatar.WriteGravatar)
        .CacheOutput(c => c.VaryByValue((context) => 
            new KeyValuePair<string, string>(
                "time", (DateTime.Now.Second % 2)
                    .ToString(CultureInfo.InvariantCulture))));
    

Anahtarın yol bölümünün büyük/küçük harfe duyarlı olduğunu belirtmek için kullanın OutputCacheOptions.UseCaseSensitivePaths . Varsayılan, büyük/küçük harfe duyarsızdır.

Diğer seçenekler için sınıfına OutputCachePolicyBuilder bakın.

Önbellek yeniden doğrulama

Önbellek yeniden doğrulama, sunucunun tam yanıt gövdesi yerine bir 304 Not Modified HTTP durum kodu döndürebileceği anlamına gelir. Bu durum kodu, istemciye istek yanıtının istemcinin daha önce aldığı yanıttan değişmediğini bildirir.

Aşağıdaki kod, önbellek yeniden doğrulamasını etkinleştirmek için üst Etag bilgi kullanımını gösterir. İstemci önceki bir yanıtın etag değerini içeren bir If-None-Match üst bilgi gönderirse ve önbellek girdisi yeniyse, sunucu tam yanıt yerine 304 Değiştirilmedi değerini döndürür:

app.MapGet("/etag", async (context) =>
{
    var etag = $"\"{Guid.NewGuid():n}\"";
    context.Response.Headers.ETag = etag;
    await Gravatar.WriteGravatar(context);

}).CacheOutput();

Önbellek yeniden doğrulamasını gerçekleştirmenin bir diğer yolu da, istemci tarafından istenen tarihle karşılaştırıldığında önbellek girdisi oluşturma tarihini denetlemektir. İstek üst bilgisi If-Modified-Since sağlandığında, önbelleğe alınan girdi daha eskiyse ve süresi dolmadıysa çıkış önbelleğe alma 304 döndürür.

önbellek yeniden doğrulama, istemciden gönderilen bu üst bilgiler yanıt olarak otomatiktir. Çıktı önbelleğini etkinleştirmenin yanı sıra bu davranışı etkinleştirmek için sunucuda özel bir yapılandırma gerekmez.

Önbellek girdilerini çıkarmak için etiketleri kullanma

Bir grup uç noktayı tanımlamak ve grubun tüm önbellek girdilerini çıkarmak için etiketleri kullanabilirsiniz. Örneğin, aşağıdaki kod URL'leri "blog" ile başlayan bir çift uç nokta oluşturur ve bunları "tag-blog" olarak etiketler:

app.MapGet("/blog", Gravatar.WriteGravatar)
    .CacheOutput(builder => builder.Tag("tag-blog")); ;
app.MapGet("/blog/post/{id}", Gravatar.WriteGravatar)
    .CacheOutput(builder => builder.Tag("tag-blog")); ;

Aynı uç nokta çifti için etiket atamanın alternatif bir yolu, ile blogbaşlayan uç noktalara uygulanan bir temel ilke tanımlamaktır:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => builder
        .With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
        .Tag("tag-blog"));
    options.AddBasePolicy(builder => builder.Tag("tag-all"));
    options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
    options.AddPolicy("NoCache", builder => builder.NoCache());
    options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});

Bir diğer alternatif de çağrısı MapGroupyapmaktır:

var blog = app.MapGroup("blog")
    .CacheOutput(builder => builder.Tag("tag-blog"));
blog.MapGet("/", Gravatar.WriteGravatar);
blog.MapGet("/post/{id}", Gravatar.WriteGravatar);

Yukarıdaki etiket atama örneklerinde her iki uç nokta da etiket tarafından tag-blog tanımlanır. Ardından bu uç noktaların önbellek girdilerini şu etikete başvuran tek bir deyimle çıkarabilirsiniz:

app.MapPost("/purge/{tag}", async (IOutputCacheStore cache, string tag) =>
{
    await cache.EvictByTagAsync(tag, default);
});

Bu kodla, öğesine https://localhost:<port>/purge/tag-blog gönderilen bir HTTP POST isteği bu uç noktalar için önbellek girdilerini kaldırır.

Tüm uç noktalar için tüm önbellek girdilerini çıkarmanın bir yolunu isteyebilirsiniz. Bunu yapmak için, aşağıdaki kodda olduğu gibi tüm uç noktalar için bir temel ilke oluşturun:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => builder
        .With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
        .Tag("tag-blog"));
    options.AddBasePolicy(builder => builder.Tag("tag-all"));
    options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
    options.AddPolicy("NoCache", builder => builder.NoCache());
    options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});

Bu temel ilke, önbellekteki her şeyi çıkarmak için "tag-all" etiketini kullanmanıza olanak tanır.

Kaynak kilitlemeyi devre dışı bırakma

Varsayılan olarak, önbellek damgalı ve gürleyen sürü riskini azaltmak için kaynak kilitleme etkinleştirilir. Daha fazla bilgi için bkz. Çıkış Önbelleğe Alma.

Kaynak kilitlemeyi devre dışı bırakmak için, aşağıdaki örnekte gösterildiği gibi bir ilke oluştururken SetLocking(false) öğesini çağırın:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => builder
        .With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
        .Tag("tag-blog"));
    options.AddBasePolicy(builder => builder.Tag("tag-all"));
    options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
    options.AddPolicy("NoCache", builder => builder.NoCache());
    options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});

Aşağıdaki örnek, bir uç nokta için kilitlememe ilkesini seçer:

app.MapGet("/nolock", Gravatar.WriteGravatar)
    .CacheOutput("NoLock");

Sınırlar

aşağıdaki özellikleri OutputCacheOptions , tüm uç noktalara uygulanan sınırları yapılandırmanıza olanak sağlar:

  • SizeLimit - En büyük önbellek depolama alanı boyutu. Bu sınıra ulaşıldığında, eski girdiler çıkarılana kadar yeni yanıtlar önbelleğe alınmaz. Varsayılan değer 100 MB'tır.
  • MaximumBodySize - Yanıt gövdesi bu sınırı aşarsa önbelleğe alınmaz. Varsayılan değer 64 MB'tır.
  • DefaultExpirationTimeSpan - İlke tarafından belirtilmediğinde geçerli olan süre sonu süresi. Varsayılan değer 60 saniyedir.

Önbellek depolama

IOutputCacheStore depolama için kullanılır. Varsayılan olarak ile MemoryCachekullanılır. Önbelleğe alınan yanıtlar işlem içinde depolanır, bu nedenle sunucu işlemi her yeniden başlatıldığında her sunucunun kaybedilen ayrı bir önbelleği vardır.

Redis Cache

Alternatif olarak Redis önbelleğini kullanabilirsiniz. Redis cache, tek tek sunucu işlemlerinden daha uzun süre çalışan paylaşılan bir önbellek aracılığıyla sunucu düğümleri arasında tutarlılık sağlar. Çıkış önbelleği için Redis'i kullanmak için:

  • Microsoft.AspNetCore.Output Önbelleğe Alma yükleyin. StackExchangeRedis NuGet paketi.

  • Çağrısı builder.Services.AddStackExchangeRedisOutputCache (değilAddStackExchangeRedisCache) ve Redis sunucusuna işaret eden bir bağlantı dizesi sağlayın.

    Örneğin:

    builder.Services.AddStackExchangeRedisOutputCache(options =>
    {
        options.Configuration = 
            builder.Configuration.GetConnectionString("MyRedisConStr");
        options.InstanceName = "SampleInstance";
    });
    
    builder.Services.AddOutputCache(options =>
    {
        options.AddBasePolicy(builder => 
            builder.Expire(TimeSpan.FromSeconds(10)));
    });
    

    Yapılandırma seçenekleri Redis tabanlı dağıtılmış önbelleğe alma seçenekleriyle aynıdır.

Çıktı önbelleğe alma ile kullanılması önerilmez IDistributedCache . IDistributedCache etiketleme için gereken atomik özelliklere sahip değildir. Redis için yerleşik desteği kullanmanızı veya temel depolama mekanizmasında doğrudan bağımlılıkları kullanarak özel IOutputCacheStore uygulamalar oluşturmanızı öneririz.

Ayrıca bkz.

Bu makalede, ASP.NET Core uygulamasında çıkış önbelleğe alma ara yazılımının nasıl yapılandırılması açıklanır. Çıkış önbelleğine giriş için bkz . Çıkış önbelleğe alma.

Çıktı önbelleğe alma ara yazılımı her tür ASP.NET Core uygulamasında kullanılabilir: Minimal API, Denetleyiciler, MVC ve Razor Sayfalar içeren Web API'leri. Örnek uygulama minimal bir API'dir, ancak göstermiş olduğu her önbelleğe alma özelliği diğer uygulama türlerinde de desteklenir.

Ara yazılımı uygulamaya ekleme

çağrısı AddOutputCacheyaparak çıkış önbelleğe alma ara yazılımını hizmet koleksiyonuna ekleyin.

ara yazılımı çağrısı UseOutputCacheyaparak istek işleme işlem hattına ekleyin.

Not

  • CORS ara yazılımı kullanan uygulamalarda, UseOutputCache sonrasında UseCorsçağrılmalıdır.
  • Razor Pages uygulamaları ve denetleyicili uygulamalar bölümünden UseOutputCache sonra UseRoutingçağrılmalıdır.
  • Çağrısı AddOutputCacheyapar ve UseOutputCache önbelleğe alma davranışını başlatmaz, önbelleğe alma özelliğini kullanılabilir hale getirir. Önbelleğe Alma yanıt verileri aşağıdaki bölümlerde gösterildiği gibi yapılandırılmalıdır.

Bir uç nokta veya sayfa yapılandırma

En düşük API uygulamaları için, aşağıdaki örneklerde gösterildiği gibi öğesini çağırarak CacheOutputveya özniteliğini uygulayarak [OutputCache] önbelleğe alma yapacak bir uç nokta yapılandırın:

app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
app.MapGet("/attribute", [OutputCache] (context) => 
    Gravatar.WriteGravatar(context));

Denetleyicili uygulamalar için özniteliğini [OutputCache] eylem yöntemine uygulayın. Pages uygulamaları için Razor özniteliğini sayfa sınıfına Razor uygulayın.

Birden çok uç nokta veya sayfa yapılandırma

Birden çok uç noktaya uygulanan önbelleğe alma yapılandırmasını belirtmek için ararken AddOutputCache ilkeler oluşturun. Belirli uç noktalar için bir ilke seçilebilirken, temel ilke bir uç nokta koleksiyonu için varsayılan önbelleğe alma yapılandırması sağlar.

Aşağıdaki vurgulanmış kod, uygulamanın tüm uç noktaları için önbelleğe alma işlemini 10 saniyelik süreyle yapılandırıyor. Süre sonu belirtilmezse, varsayılan olarak bir dakika olur.

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => 
        builder.Expire(TimeSpan.FromSeconds(10)));
    options.AddPolicy("Expire20", builder => 
        builder.Expire(TimeSpan.FromSeconds(20)));
    options.AddPolicy("Expire30", builder => 
        builder.Expire(TimeSpan.FromSeconds(30)));
});

Aşağıdaki vurgulanmış kod, her birinde farklı bir süre sonu belirten iki ilke oluşturur. Seçilen uç noktalar 20 saniyelik süre sonunu, diğerleri ise 30 saniyelik süre sonunu kullanabilir.

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => 
        builder.Expire(TimeSpan.FromSeconds(10)));
    options.AddPolicy("Expire20", builder => 
        builder.Expire(TimeSpan.FromSeconds(20)));
    options.AddPolicy("Expire30", builder => 
        builder.Expire(TimeSpan.FromSeconds(30)));
});

Yöntemini çağırırken CacheOutput veya özniteliğini kullanarak [OutputCache] uç nokta için bir ilke seçebilirsiniz:

app.MapGet("/20", Gravatar.WriteGravatar).CacheOutput("Expire20");
app.MapGet("/30", [OutputCache(PolicyName = "Expire30")] (context) => 
    Gravatar.WriteGravatar(context));

Denetleyicili uygulamalar için özniteliğini [OutputCache] eylem yöntemine uygulayın. Pages uygulamaları için Razor özniteliğini sayfa sınıfına Razor uygulayın.

Varsayılan çıkış önbelleğe alma ilkesi

Varsayılan olarak, çıktı önbelleğe alma şu kuralları izler:

  • Yalnızca HTTP 200 yanıtları önbelleğe alınır.
  • Yalnızca HTTP GET veya HEAD istekleri önbelleğe alınır.
  • Ayarlanan cookieyanıtlar önbelleğe alınmaz.
  • Kimliği doğrulanmış isteklere verilen yanıtlar önbelleğe alınmaz.

Aşağıdaki kod, varsayılan önbelleğe alma kurallarının tümünü uygulamanın tüm uç noktalarına uygular:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => builder.Cache());
});

Varsayılan ilkeyi geçersiz kılma

Aşağıdaki kod, varsayılan kuralların nasıl geçersiz kılınacaklarını gösterir. Aşağıdaki özel ilke kodunda vurgulanan satırlar, HTTP POST yöntemleri ve HTTP 301 yanıtları için önbelleğe almayı etkinleştirir:

using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.Primitives;

namespace OCMinimal;

public sealed class MyCustomPolicy : IOutputCachePolicy
{
    public static readonly MyCustomPolicy Instance = new();

    private MyCustomPolicy()
    {
    }

    ValueTask IOutputCachePolicy.CacheRequestAsync(
        OutputCacheContext context, 
        CancellationToken cancellationToken)
    {
        var attemptOutputCaching = AttemptOutputCaching(context);
        context.EnableOutputCaching = true;
        context.AllowCacheLookup = attemptOutputCaching;
        context.AllowCacheStorage = attemptOutputCaching;
        context.AllowLocking = true;

        // Vary by any query by default
        context.CacheVaryByRules.QueryKeys = "*";

        return ValueTask.CompletedTask;
    }

    ValueTask IOutputCachePolicy.ServeFromCacheAsync
        (OutputCacheContext context, CancellationToken cancellationToken)
    {
        return ValueTask.CompletedTask;
    }

    ValueTask IOutputCachePolicy.ServeResponseAsync
        (OutputCacheContext context, CancellationToken cancellationToken)
    {
        var response = context.HttpContext.Response;

        // Verify existence of cookie headers
        if (!StringValues.IsNullOrEmpty(response.Headers.SetCookie))
        {
            context.AllowCacheStorage = false;
            return ValueTask.CompletedTask;
        }

        // Check response code
        if (response.StatusCode != StatusCodes.Status200OK && 
            response.StatusCode != StatusCodes.Status301MovedPermanently)
        {
            context.AllowCacheStorage = false;
            return ValueTask.CompletedTask;
        }

        return ValueTask.CompletedTask;
    }

    private static bool AttemptOutputCaching(OutputCacheContext context)
    {
        // Check if the current request fulfills the requirements
        // to be cached
        var request = context.HttpContext.Request;

        // Verify the method
        if (!HttpMethods.IsGet(request.Method) && 
            !HttpMethods.IsHead(request.Method) && 
            !HttpMethods.IsPost(request.Method))
        {
            return false;
        }

        // Verify existence of authorization headers
        if (!StringValues.IsNullOrEmpty(request.Headers.Authorization) || 
            request.HttpContext.User?.Identity?.IsAuthenticated == true)
        {
            return false;
        }

        return true;
    }
}

Bu özel ilkeyi kullanmak için adlandırılmış bir ilke oluşturun:

builder.Services.AddOutputCache(options =>
{
    options.AddPolicy("CachePost", MyCustomPolicy.Instance);
});

Bir uç nokta için adlandırılmış ilkeyi seçin:

app.MapPost("/cachedpost", Gravatar.WriteGravatar)
    .CacheOutput("CachePost");

Alternatif varsayılan ilke geçersiz kılma

Alternatif olarak, özel ilke sınıfında aşağıdaki değişikliklerle bir örneği başlatmak için Bağımlılık Ekleme (DI) kullanın:

  • Özel oluşturucu yerine ortak oluşturucu.
  • Instance Özel ilke sınıfında özelliğini ortadan kaldırın.

Örneğin:

public sealed class MyCustomPolicy2 : IOutputCachePolicy
{

    public MyCustomPolicy2()
    {
    }

Sınıfın geri kalanı daha önce gösterildiği gibi aynıdır. Aşağıdaki örnekte gösterildiği gibi özel ilkeyi ekleyin:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => 
        builder.AddPolicy<MyCustomPolicy2>(), true);
});

Yukarıdaki kod, özel ilke sınıfının örneğini oluşturmak için DI kullanır. Oluşturucudaki tüm genel bağımsız değişkenler çözümlenir.

Özel ilkeyi temel ilke olarak kullanırken, temel ilkenin geçerli olması gereken uç noktalarda (bağımsız değişken olmadan) çağırmayın OutputCache() . Çağırma OutputCache() , varsayılan ilkeyi uç noktaya ekler.

Önbellek anahtarını belirtme

Varsayılan olarak, URL'nin her bölümü bir önbellek girdisinin anahtarı olarak (şema, konak, bağlantı noktası, yol ve sorgu dizesi) eklenir. Ancak, önbellek anahtarını açıkça denetlemek isteyebilirsiniz. Örneğin, yalnızca sorgu dizesinin her benzersiz değeri culture için benzersiz bir yanıt döndüren bir uç noktanız olduğunu varsayalım. URL'nin diğer bölümlerindeki varyasyon (diğer sorgu dizeleri gibi) farklı önbellek girdilerine neden olmamalıdır. Aşağıdaki vurgulanmış kodda gösterildiği gibi bir ilkede bu tür kuralları belirtebilirsiniz:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => builder
        .With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
        .Tag("tag-blog"));
    options.AddBasePolicy(builder => builder.Tag("tag-all"));
    options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
    options.AddPolicy("NoCache", builder => builder.NoCache());
    options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});

Ardından bir uç nokta için ilkeyi VaryByQuery seçebilirsiniz:

app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput("Query");

Önbellek anahtarını denetleme seçeneklerinden bazıları şunlardır:

  • SetVaryByQuery - Önbellek anahtarına eklenecek bir veya daha fazla sorgu dizesi adı belirtin.

  • SetVaryByHeader - Önbellek anahtarına eklenecek bir veya daha fazla HTTP üst bilgisi belirtin.

  • VaryByValue- Önbellek anahtarına eklenecek değeri belirtin. Aşağıdaki örnek, geçerli sunucu saatinin saniye olarak tek mi yoksa çift mi olduğunu gösteren bir değer kullanır. Yeni yanıt yalnızca saniye sayısı tekten çifte, hatta teke geçtiğinde oluşturulur.

    app.MapGet("/varybyvalue", Gravatar.WriteGravatar)
        .CacheOutput(c => c.VaryByValue((context) => 
            new KeyValuePair<string, string>(
                "time", (DateTime.Now.Second % 2)
                    .ToString(CultureInfo.InvariantCulture))));
    

Anahtarın yol bölümünün büyük/küçük harfe duyarlı olduğunu belirtmek için kullanın OutputCacheOptions.UseCaseSensitivePaths . Varsayılan, büyük/küçük harfe duyarsızdır.

Diğer seçenekler için sınıfına OutputCachePolicyBuilder bakın.

Önbellek yeniden doğrulama

Önbellek yeniden doğrulama, sunucunun tam yanıt gövdesi yerine bir 304 Not Modified HTTP durum kodu döndürebileceği anlamına gelir. Bu durum kodu, istemciye istek yanıtının istemcinin daha önce aldığı yanıttan değişmediğini bildirir.

Aşağıdaki kod, önbellek yeniden doğrulamasını etkinleştirmek için üst Etag bilgi kullanımını gösterir. İstemci önceki bir yanıtın etag değerini içeren bir If-None-Match üst bilgi gönderirse ve önbellek girdisi yeniyse, sunucu tam yanıt yerine 304 Değiştirilmedi değerini döndürür:

app.MapGet("/etag", async (context) =>
{
    var etag = $"\"{Guid.NewGuid():n}\"";
    context.Response.Headers.ETag = etag;
    await Gravatar.WriteGravatar(context);

}).CacheOutput();

Önbellek yeniden doğrulamasını gerçekleştirmenin bir diğer yolu da, istemci tarafından istenen tarihle karşılaştırıldığında önbellek girdisi oluşturma tarihini denetlemektir. İstek üst bilgisi If-Modified-Since sağlandığında, önbelleğe alınan girdi daha eskiyse ve süresi dolmadıysa çıkış önbelleğe alma 304 döndürür.

önbellek yeniden doğrulama, istemciden gönderilen bu üst bilgiler yanıt olarak otomatiktir. Çıktı önbelleğini etkinleştirmenin yanı sıra bu davranışı etkinleştirmek için sunucuda özel bir yapılandırma gerekmez.

Önbellek girdilerini çıkarmak için etiketleri kullanma

Bir grup uç noktayı tanımlamak ve grubun tüm önbellek girdilerini çıkarmak için etiketleri kullanabilirsiniz. Örneğin, aşağıdaki kod URL'leri "blog" ile başlayan bir çift uç nokta oluşturur ve bunları "tag-blog" olarak etiketler:

app.MapGet("/blog", Gravatar.WriteGravatar)
    .CacheOutput(builder => builder.Tag("tag-blog")); ;
app.MapGet("/blog/post/{id}", Gravatar.WriteGravatar)
    .CacheOutput(builder => builder.Tag("tag-blog")); ;

Aynı uç nokta çifti için etiket atamanın alternatif bir yolu, ile blogbaşlayan uç noktalara uygulanan bir temel ilke tanımlamaktır:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => builder
        .With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
        .Tag("tag-blog"));
    options.AddBasePolicy(builder => builder.Tag("tag-all"));
    options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
    options.AddPolicy("NoCache", builder => builder.NoCache());
    options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});

Bir diğer alternatif de çağrısı MapGroupyapmaktır:

var blog = app.MapGroup("blog")
    .CacheOutput(builder => builder.Tag("tag-blog"));
blog.MapGet("/", Gravatar.WriteGravatar);
blog.MapGet("/post/{id}", Gravatar.WriteGravatar);

Yukarıdaki etiket atama örneklerinde her iki uç nokta da etiket tarafından tag-blog tanımlanır. Ardından bu uç noktaların önbellek girdilerini şu etikete başvuran tek bir deyimle çıkarabilirsiniz:

app.MapPost("/purge/{tag}", async (IOutputCacheStore cache, string tag) =>
{
    await cache.EvictByTagAsync(tag, default);
});

Bu kodla, gönderilen https://localhost:<port>/purge/tag-blog bir HTTP POST isteği bu uç noktalar için önbellek girdilerini çıkaracaktır.

Tüm uç noktalar için tüm önbellek girdilerini çıkarmanın bir yolunu isteyebilirsiniz. Bunu yapmak için, aşağıdaki kodda olduğu gibi tüm uç noktalar için bir temel ilke oluşturun:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => builder
        .With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
        .Tag("tag-blog"));
    options.AddBasePolicy(builder => builder.Tag("tag-all"));
    options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
    options.AddPolicy("NoCache", builder => builder.NoCache());
    options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});

Bu temel ilke, önbellekteki her şeyi çıkarmak için "tag-all" etiketini kullanmanıza olanak tanır.

Kaynak kilitlemeyi devre dışı bırakma

Varsayılan olarak, önbellek damgalı ve gürleyen sürü riskini azaltmak için kaynak kilitleme etkinleştirilir. Daha fazla bilgi için bkz. Çıkış Önbelleğe Alma.

Kaynak kilitlemeyi devre dışı bırakmak için, aşağıdaki örnekte gösterildiği gibi bir ilke oluştururken SetLocking(false) öğesini çağırın:

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => builder
        .With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
        .Tag("tag-blog"));
    options.AddBasePolicy(builder => builder.Tag("tag-all"));
    options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
    options.AddPolicy("NoCache", builder => builder.NoCache());
    options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});

Aşağıdaki örnek, bir uç nokta için kilitlememe ilkesini seçer:

app.MapGet("/nolock", Gravatar.WriteGravatar)
    .CacheOutput("NoLock");

Sınırlar

aşağıdaki özellikleri OutputCacheOptions , tüm uç noktalara uygulanan sınırları yapılandırmanıza olanak sağlar:

  • SizeLimit - En büyük önbellek depolama alanı boyutu. Bu sınıra ulaşıldığında, eski girdiler çıkarılana kadar yeni yanıtlar önbelleğe alınmaz. Varsayılan değer 100 MB'tır.
  • MaximumBodySize - Yanıt gövdesi bu sınırı aşarsa önbelleğe alınmaz. Varsayılan değer 64 MB'tır.
  • DefaultExpirationTimeSpan - İlke tarafından belirtilmediğinde geçerli olan süre sonu süresi. Varsayılan değer 60 saniyedir.

Önbellek depolama

IOutputCacheStore depolama için kullanılır. Varsayılan olarak ile MemoryCachekullanılır. Çıktı önbelleğe alma ile kullanılması önerilmez IDistributedCache . IDistributedCache etiketleme için gereken atomik özelliklere sahip değildir. Redis gibi temel depolama mekanizmasında doğrudan bağımlılıkları kullanarak özel IOutputCacheStore uygulamalar oluşturmanızı öneririz. Veya .NET 8'de Redis cache için yerleşik desteği kullanın ..

Ayrıca bkz.