Middleware penembolokan output di ASP.NET Core
Oleh Tom Dykstra
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.
Peringatan
Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.
Penting
Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.
Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.
Artikel ini menjelaskan cara mengonfigurasi middleware penembolokan output di aplikasi ASP.NET Core. Untuk pengenalan penembolokan output, lihat Penembolokan output.
Middleware penembolokan output dapat digunakan di semua jenis aplikasi ASP.NET Core: API Minimal, API Web dengan pengontrol, MVC, dan Razor Pages. Contoh kode disediakan untuk API minimal dan API berbasis pengontrol. Contoh API berbasis pengontrol menunjukkan cara menggunakan atribut untuk mengonfigurasi penembolokan. Atribut ini juga dapat digunakan di aplikasi MVC dan Razor Pages.
Contoh kode merujuk ke kelas Gravatar yang menghasilkan gambar dan menyediakan tanggal dan waktu "dihasilkan pada". Kelas ditentukan dan hanya digunakan di aplikasi sampel. Tujuannya adalah untuk memudahkan untuk melihat kapan output yang di-cache sedang digunakan. Untuk informasi selengkapnya, lihat Cara mengunduh sampel dan Arahan praproscessor dalam kode sampel.
Menambahkan middleware ke aplikasi
Tambahkan middleware penembolokan output ke kumpulan layanan dengan memanggil AddOutputCache.
Tambahkan middleware ke alur pemrosesan permintaan dengan memanggil UseOutputCache.
Contohnya:
builder.Services.AddOutputCache();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseOutputCache();
app.UseAuthorization();
Memanggil AddOutputCache
dan UseOutputCache
tidak memulai perilaku penembolokan, membuat penembolokan tersedia. Untuk membuat respons cache aplikasi, penembolokan harus dikonfigurasi seperti yang ditunjukkan di bagian berikut.
Catatan
- Dalam aplikasi yang menggunakan middleware CORS,
UseOutputCache
harus dipanggil setelah UseCors. - Di Razor aplikasi dan aplikasi Pages dengan pengontrol,
UseOutputCache
harus dipanggil setelahUseRouting
.
Mengonfigurasi satu titik akhir atau halaman
Untuk aplikasi API minimal, konfigurasikan titik akhir untuk melakukan penembolokan dengan memanggil CacheOutput
, atau dengan menerapkan [OutputCache]
atribut , seperti yang ditunjukkan dalam contoh berikut:
app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
app.MapGet("/attribute", [OutputCache] (context) =>
Gravatar.WriteGravatar(context));
Untuk aplikasi dengan pengontrol, terapkan [OutputCache]
atribut ke metode tindakan seperti yang ditunjukkan di sini:
[ApiController]
[Route("/[controller]")]
[OutputCache]
public class CachedController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Untuk Razor aplikasi Pages, terapkan atribut ke Razor kelas halaman.
Mengonfigurasi beberapa titik akhir atau halaman
Buat kebijakan saat memanggil AddOutputCache
untuk menentukan konfigurasi penembolokan yang berlaku untuk beberapa titik akhir. Kebijakan dapat dipilih untuk titik akhir tertentu, sementara kebijakan dasar menyediakan konfigurasi penembolokan default untuk kumpulan titik akhir.
Kode yang disorot berikut mengonfigurasi penembolokan untuk semua titik akhir aplikasi, dengan waktu kedaluwarsa 10 detik. Jika waktu kedaluwarsa tidak ditentukan, waktu defaultnya adalah satu menit.
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)));
});
Kode yang disorot berikut membuat dua kebijakan, masing-masing menentukan waktu kedaluwarsa yang berbeda. Titik akhir yang dipilih dapat menggunakan kedaluwarsa 20 detik, dan yang lain dapat menggunakan kedaluwarsa 30 detik.
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)));
});
Anda dapat memilih kebijakan untuk titik akhir saat memanggil CacheOutput
metode atau menggunakan [OutputCache]
atribut .
Dalam aplikasi API minimal, kode berikut mengonfigurasi satu titik akhir dengan kedaluwarsa 20 detik dan satu dengan kedaluwarsa 30 detik:
app.MapGet("/20", Gravatar.WriteGravatar).CacheOutput("Expire20");
app.MapGet("/30", [OutputCache(PolicyName = "Expire30")] (context) =>
Gravatar.WriteGravatar(context));
Untuk aplikasi dengan pengontrol, terapkan [OutputCache]
atribut ke metode tindakan untuk memilih kebijakan:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "Expire20")]
public class Expire20Controller : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Untuk Razor aplikasi Pages, terapkan atribut ke Razor kelas halaman.
Kebijakan penembolokan output default
Secara default, penembolokan output mengikuti aturan berikut:
- Hanya respons HTTP 200 yang di-cache.
- Hanya permintaan HTTP GET atau HEAD yang di-cache.
- Respons yang mengatur cookie tidak di-cache.
- Respons terhadap permintaan terautentikasi tidak di-cache.
Kode berikut menerapkan semua aturan penembolokan default ke semua titik akhir aplikasi:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Cache());
});
Mengesampingkan kebijakan default
Kode berikut menunjukkan cara mengambil alih aturan default. Baris yang disorot dalam kode kebijakan kustom berikut memungkinkan penembolokan untuk metode HTTP POST dan respons HTTP 301:
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;
}
}
Untuk menggunakan kebijakan kustom ini, buat kebijakan bernama:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", MyCustomPolicy.Instance);
});
Dan pilih kebijakan bernama untuk titik akhir. Kode berikut memilih kebijakan kustom untuk titik akhir di aplikasi API minimal:
app.MapPost("/cachedpost", Gravatar.WriteGravatar)
.CacheOutput("CachePost");
Kode berikut melakukan hal yang sama untuk tindakan pengontrol:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "CachePost")]
public class PostController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Penggantian kebijakan default alternatif
Atau, gunakan Dependency Injection (DI) untuk menginisialisasi instans, dengan perubahan berikut pada kelas kebijakan kustom:
- Konstruktor publik alih-alih konstruktor privat.
- Hilangkan
Instance
properti di kelas kebijakan kustom.
Contohnya:
public sealed class MyCustomPolicy2 : IOutputCachePolicy
{
public MyCustomPolicy2()
{
}
Sisa kelas sama seperti yang ditunjukkan sebelumnya. Tambahkan kebijakan kustom seperti yang ditunjukkan dalam contoh berikut:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder =>
builder.AddPolicy<MyCustomPolicy2>(), true);
});
Kode sebelumnya menggunakan DI untuk membuat instans kelas kebijakan kustom. Setiap argumen publik dalam konstruktor diselesaikan.
Saat menggunakan kebijakan kustom sebagai kebijakan dasar, jangan panggil OutputCache()
(tanpa argumen) atau gunakan [OutputCache]
atribut pada titik akhir apa pun yang harus diterapkan oleh kebijakan dasar. Memanggil OutputCache()
atau menggunakan atribut menambahkan kebijakan default ke titik akhir.
Tentukan kunci cache
Secara default, setiap bagian URL disertakan sebagai kunci ke entri cache, yaitu, skema, host, port, jalur, dan string kueri. Namun, Anda mungkin ingin mengontrol kunci cache secara eksplisit. Misalnya, Anda memiliki titik akhir yang mengembalikan respons unik hanya untuk setiap nilai culture
unik string kueri. Variasi di bagian lain URL, seperti string kueri lainnya, tidak boleh menghasilkan entri cache yang berbeda. Anda dapat menentukan aturan tersebut dalam kebijakan, seperti yang ditunjukkan dalam kode yang disorot berikut:
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));
});
Anda kemudian dapat memilih VaryByQuery
kebijakan untuk titik akhir. Dalam aplikasi API minimal, kode berikut memilih VaryByQuery
kebijakan untuk titik akhir yang mengembalikan respons unik hanya untuk setiap nilai unik string culture
kueri:
app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput("Query");
Kode berikut melakukan hal yang sama untuk tindakan pengontrol:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "Query")]
public class QueryController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Berikut adalah beberapa opsi untuk mengontrol kunci cache:
SetVaryByQuery - Tentukan satu atau beberapa nama string kueri untuk ditambahkan ke kunci cache.
SetVaryByHeader - Tentukan satu atau beberapa header HTTP untuk ditambahkan ke kunci cache.
VaryByValue- Tentukan nilai untuk ditambahkan ke kunci cache. Contoh berikut menggunakan nilai yang menunjukkan apakah waktu server saat ini dalam detik ganjil atau genap. Respons baru dihasilkan hanya ketika jumlah detik berubah dari ganjil ke genap atau bahkan ke ganjil.
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)); options.AddPolicy("VaryByValue", builder => builder.VaryByValue((context) => new KeyValuePair<string, string>( "time", (DateTime.Now.Second % 2) .ToString(CultureInfo.InvariantCulture)))); });
Gunakan OutputCacheOptions.UseCaseSensitivePaths untuk menentukan bahwa bagian jalur kunci peka huruf besar/kecil. Defaultnya adalah tidak peka huruf besar/kecil.
Untuk opsi lainnya, lihat OutputCachePolicyBuilder kelas .
Validasi ulang cache
Validasi ulang cache berarti server dapat mengembalikan 304 Not Modified
kode status HTTP alih-alih isi respons penuh. Kode status ini memberi tahu klien bahwa respons terhadap permintaan tidak berubah dari apa yang diterima klien sebelumnya.
Kode berikut mengilustrasikan penggunaan Etag
header untuk mengaktifkan validasi ulang cache. Jika klien mengirim If-None-Match
header dengan nilai etag dari respons sebelumnya, dan entri cache segar, server mengembalikan 304 Tidak Dimodifikasi alih-alih respons penuh. Berikut cara mengatur nilai etag dalam kebijakan, di aplikasi API Minimal:
app.MapGet("/etag", async (context) =>
{
var etag = $"\"{Guid.NewGuid():n}\"";
context.Response.Headers.ETag = etag;
await Gravatar.WriteGravatar(context);
}).CacheOutput();
Dan berikut cara mengatur nilai etag dalam API berbasis pengontrol:
[ApiController]
[Route("/[controller]")]
[OutputCache]
public class EtagController : ControllerBase
{
public async Task GetAsync()
{
var etag = $"\"{Guid.NewGuid():n}\"";
HttpContext.Response.Headers.ETag = etag;
await Gravatar.WriteGravatar(HttpContext);
}
}
Cara lain untuk melakukan validasi ulang cache adalah dengan memeriksa tanggal pembuatan entri cache dibandingkan dengan tanggal yang diminta oleh klien. Saat header If-Modified-Since
permintaan disediakan, penembolokan output mengembalikan 304 jika entri yang di-cache lebih lama dan tidak kedaluwarsa.
Validasi ulang cache otomatis sebagai respons terhadap header ini yang dikirim dari klien. Tidak ada konfigurasi khusus yang diperlukan pada server untuk mengaktifkan perilaku ini, selain mengaktifkan penembolokan output.
Menggunakan tag untuk mengeluarkan entri cache
Anda dapat menggunakan tag untuk mengidentifikasi sekelompok titik akhir dan mengeluarkan semua entri cache untuk grup. Misalnya, kode API minimal berikut membuat sepasang titik akhir yang URL-nya dimulai dengan "blog", dan menandainya "tag-blog":
app.MapGet("/blog", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog"));
app.MapGet("/blog/post/{id}", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog"));
Kode berikut menunjukkan cara menetapkan tag ke titik akhir dalam API berbasis pengontrol:
[ApiController]
[Route("/[controller]")]
[OutputCache(Tags = new[] { "tag-blog", "tag-all" })]
public class TagEndpointController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Cara alternatif untuk menetapkan tag untuk titik akhir dengan rute yang dimulai dengan adalah dengan blog
menentukan kebijakan dasar yang berlaku untuk semua titik akhir dengan rute tersebut. Kode berikut menunjukkan cara melakukannya:
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));
});
Alternatif lain untuk aplikasi API minimal adalah memanggil MapGroup
:
var blog = app.MapGroup("blog")
.CacheOutput(builder => builder.Tag("tag-blog"));
blog.MapGet("/", Gravatar.WriteGravatar);
blog.MapGet("/post/{id}", Gravatar.WriteGravatar);
Dalam contoh penetapan tag sebelumnya, kedua titik akhir diidentifikasi oleh tag-blog
tag. Anda kemudian dapat mengeluarkan entri cache untuk titik akhir tersebut dengan satu pernyataan yang mereferensikan tag tersebut:
app.MapPost("/purge/{tag}", async (IOutputCacheStore cache, string tag) =>
{
await cache.EvictByTagAsync(tag, default);
});
Dengan kode ini, permintaan HTTP POST yang dikirim untuk https://localhost:<port>/purge/tag-blog
mengeluarkan entri cache untuk titik akhir ini.
Anda mungkin ingin cara untuk mengeluarkan semua entri cache untuk semua titik akhir. Untuk melakukannya, buat kebijakan dasar untuk semua titik akhir seperti yang dilakukan kode berikut:
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));
});
Kebijakan dasar ini memungkinkan Anda menggunakan tag "tag-all" untuk mengeluarkan semuanya dalam cache.
Menonaktifkan penguncian sumber daya
Secara default, penguncian sumber daya diaktifkan untuk mengurangi risiko cache stampede dan kawanan guntur. Untuk informasi selengkapnya, lihat Penembolokan Output.
Untuk menonaktifkan penguncian sumber daya, panggil SetLocking(false) saat membuat kebijakan, seperti yang ditunjukkan dalam contoh berikut:
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));
});
Contoh berikut memilih kebijakan tanpa penguncian untuk titik akhir di aplikasi API minimal:
app.MapGet("/nolock", Gravatar.WriteGravatar)
.CacheOutput("NoLock");
Dalam API berbasis pengontrol, gunakan atribut untuk memilih kebijakan:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "NoLock")]
public class NoLockController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Batas
Properti OutputCacheOptions berikut memungkinkan Anda mengonfigurasi batas yang berlaku untuk semua titik akhir:
- SizeLimit - Ukuran maksimum penyimpanan cache. Ketika batas ini tercapai, tidak ada respons baru yang di-cache hingga entri yang lebih lama dikeluarkan. Nilai defaultnya adalah 100 MB.
- MaximumBodySize - Jika isi respons melebihi batas ini, isi tidak di-cache. Nilai defaultnya adalah 64 MB.
- DefaultExpirationTimeSpan - Durasi waktu kedaluwarsa yang berlaku ketika tidak ditentukan oleh kebijakan. Nilai defaultnya adalah 60 detik.
Penyimpanan cache
IOutputCacheStore digunakan untuk penyimpanan. Secara default digunakan dengan MemoryCache. Respons cache disimpan dalam proses, sehingga setiap server memiliki cache terpisah yang hilang setiap kali proses server dimulai ulang.
Cache Redis
Alternatifnya adalah menggunakan cache Redis . Cache Redis memberikan konsistensi antara simpul server melalui cache bersama yang keluar dari proses server individual. Untuk menggunakan Redis untuk penembolokan output:
Instal paket NuGet Microsoft.AspNetCore.OutputCaching.StackExchangeRedis.
Panggil
builder.Services.AddStackExchangeRedisOutputCache
(bukanAddStackExchangeRedisCache
), dan berikan string koneksi yang menunjuk ke server Redis.Contohnya:
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))); });
options.Configuration
- String koneksi ke server Redis lokal atau ke penawaran yang dihosting seperti Azure Cache for Redis. Misalnya,<instance_name>.redis.cache.windows.net:6380,password=<password>,ssl=True,abortConnect=False
untuk Azure cache for Redis.options.InstanceName
- Opsional, menentukan partisi logis untuk cache.
Opsi konfigurasi identik dengan opsi penembolokan terdistribusi berbasis Redis.
IDistributedCache
tidak disarankan
Kami tidak merekomendasikan IDistributedCache untuk digunakan dengan penembolokan output. IDistributedCache
tidak memiliki fitur atomik, yang diperlukan untuk pemberian tag. Kami menyarankan agar Anda menggunakan dukungan bawaan untuk Redis atau membuat implementasi kustom IOutputCacheStore dengan menggunakan dependensi langsung pada mekanisme penyimpanan yang mendasar.
Lihat juga
Artikel ini menjelaskan cara mengonfigurasi middleware penembolokan output di aplikasi ASP.NET Core. Untuk pengenalan penembolokan output, lihat Penembolokan output.
Middleware penembolokan output dapat digunakan di semua jenis aplikasi ASP.NET Core: API Minimal, API Web dengan pengontrol, MVC, dan Razor Pages. Aplikasi sampel adalah API Minimal, tetapi setiap fitur penembolokan yang diilustrasikannya juga didukung di jenis aplikasi lain.
Menambahkan middleware ke aplikasi
Tambahkan middleware penembolokan output ke kumpulan layanan dengan memanggil AddOutputCache.
Tambahkan middleware ke alur pemrosesan permintaan dengan memanggil UseOutputCache.
Catatan
- Dalam aplikasi yang menggunakan middleware CORS,
UseOutputCache
harus dipanggil setelah UseCors. - Di Razor aplikasi dan aplikasi Pages dengan pengontrol,
UseOutputCache
harus dipanggil setelahUseRouting
. - Memanggil
AddOutputCache
danUseOutputCache
tidak memulai perilaku penembolokan, membuat penembolokan tersedia. Data respons penembolokan harus dikonfigurasi seperti yang ditunjukkan di bagian berikut.
Mengonfigurasi satu titik akhir atau halaman
Untuk aplikasi API minimal, konfigurasikan titik akhir untuk melakukan penembolokan dengan memanggil CacheOutput
, atau dengan menerapkan [OutputCache]
atribut , seperti yang ditunjukkan dalam contoh berikut:
app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
app.MapGet("/attribute", [OutputCache] (context) =>
Gravatar.WriteGravatar(context));
Untuk aplikasi dengan pengontrol, terapkan [OutputCache]
atribut ke metode tindakan. Untuk Razor aplikasi Pages, terapkan atribut ke Razor kelas halaman.
Mengonfigurasi beberapa titik akhir atau halaman
Buat kebijakan saat memanggil AddOutputCache
untuk menentukan konfigurasi penembolokan yang berlaku untuk beberapa titik akhir. Kebijakan dapat dipilih untuk titik akhir tertentu, sementara kebijakan dasar menyediakan konfigurasi penembolokan default untuk kumpulan titik akhir.
Kode yang disorot berikut mengonfigurasi penembolokan untuk semua titik akhir aplikasi, dengan waktu kedaluwarsa 10 detik. Jika waktu kedaluwarsa tidak ditentukan, waktu defaultnya adalah satu menit.
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)));
});
Kode yang disorot berikut membuat dua kebijakan, masing-masing menentukan waktu kedaluwarsa yang berbeda. Titik akhir yang dipilih dapat menggunakan kedaluwarsa 20 detik, dan yang lain dapat menggunakan kedaluwarsa 30 detik.
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)));
});
Anda dapat memilih kebijakan untuk titik akhir saat memanggil CacheOutput
metode atau menggunakan [OutputCache]
atribut :
app.MapGet("/20", Gravatar.WriteGravatar).CacheOutput("Expire20");
app.MapGet("/30", [OutputCache(PolicyName = "Expire30")] (context) =>
Gravatar.WriteGravatar(context));
Untuk aplikasi dengan pengontrol, terapkan [OutputCache]
atribut ke metode tindakan. Untuk Razor aplikasi Pages, terapkan atribut ke Razor kelas halaman.
Kebijakan penembolokan output default
Secara default, penembolokan output mengikuti aturan berikut:
- Hanya respons HTTP 200 yang di-cache.
- Hanya permintaan HTTP GET atau HEAD yang di-cache.
- Respons yang mengatur cookie tidak di-cache.
- Respons terhadap permintaan terautentikasi tidak di-cache.
Kode berikut menerapkan semua aturan penembolokan default ke semua titik akhir aplikasi:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Cache());
});
Mengesampingkan kebijakan default
Kode berikut menunjukkan cara mengambil alih aturan default. Baris yang disorot dalam kode kebijakan kustom berikut memungkinkan penembolokan untuk metode HTTP POST dan respons HTTP 301:
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;
}
}
Untuk menggunakan kebijakan kustom ini, buat kebijakan bernama:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", MyCustomPolicy.Instance);
});
Dan pilih kebijakan bernama untuk titik akhir:
app.MapPost("/cachedpost", Gravatar.WriteGravatar)
.CacheOutput("CachePost");
Penggantian kebijakan default alternatif
Atau, gunakan Dependency Injection (DI) untuk menginisialisasi instans, dengan perubahan berikut pada kelas kebijakan kustom:
- Konstruktor publik alih-alih konstruktor privat.
- Hilangkan
Instance
properti di kelas kebijakan kustom.
Contohnya:
public sealed class MyCustomPolicy2 : IOutputCachePolicy
{
public MyCustomPolicy2()
{
}
Sisa kelas sama seperti yang ditunjukkan sebelumnya. Tambahkan kebijakan kustom seperti yang ditunjukkan dalam contoh berikut:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder =>
builder.AddPolicy<MyCustomPolicy2>(), true);
});
Kode sebelumnya menggunakan DI untuk membuat instans kelas kebijakan kustom. Setiap argumen publik dalam konstruktor diselesaikan.
Saat menggunakan kebijakan kustom sebagai kebijakan dasar, jangan panggil OutputCache()
(tanpa argumen) pada titik akhir apa pun yang harus diterapkan oleh kebijakan dasar. OutputCache()
Panggilan menambahkan kebijakan default ke titik akhir.
Tentukan kunci cache
Secara default, setiap bagian URL disertakan sebagai kunci ke entri cache, yaitu, skema, host, port, jalur, dan string kueri. Namun, Anda mungkin ingin mengontrol kunci cache secara eksplisit. Misalnya, Anda memiliki titik akhir yang mengembalikan respons unik hanya untuk setiap nilai culture
unik string kueri. Variasi di bagian lain URL, seperti string kueri lainnya, tidak boleh menghasilkan entri cache yang berbeda. Anda dapat menentukan aturan tersebut dalam kebijakan, seperti yang ditunjukkan dalam kode yang disorot berikut:
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));
});
Anda kemudian dapat memilih VaryByQuery
kebijakan untuk titik akhir:
app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput("Query");
Berikut adalah beberapa opsi untuk mengontrol kunci cache:
SetVaryByQuery - Tentukan satu atau beberapa nama string kueri untuk ditambahkan ke kunci cache.
SetVaryByHeader - Tentukan satu atau beberapa header HTTP untuk ditambahkan ke kunci cache.
VaryByValue- Tentukan nilai untuk ditambahkan ke kunci cache. Contoh berikut menggunakan nilai yang menunjukkan apakah waktu server saat ini dalam detik ganjil atau genap. Respons baru dihasilkan hanya ketika jumlah detik berubah dari ganjil ke genap atau bahkan ke ganjil.
app.MapGet("/varybyvalue", Gravatar.WriteGravatar) .CacheOutput(c => c.VaryByValue((context) => new KeyValuePair<string, string>( "time", (DateTime.Now.Second % 2) .ToString(CultureInfo.InvariantCulture))));
Gunakan OutputCacheOptions.UseCaseSensitivePaths untuk menentukan bahwa bagian jalur kunci peka huruf besar/kecil. Defaultnya adalah tidak peka huruf besar/kecil.
Untuk opsi lainnya, lihat OutputCachePolicyBuilder kelas .
Validasi ulang cache
Validasi ulang cache berarti server dapat mengembalikan 304 Not Modified
kode status HTTP alih-alih isi respons penuh. Kode status ini memberi tahu klien bahwa respons terhadap permintaan tidak berubah dari apa yang diterima klien sebelumnya.
Kode berikut mengilustrasikan penggunaan Etag
header untuk mengaktifkan validasi ulang cache. Jika klien mengirim If-None-Match
header dengan nilai etag dari respons sebelumnya, dan entri cache segar, server mengembalikan 304 Tidak Dimodifikasi alih-alih respons penuh:
app.MapGet("/etag", async (context) =>
{
var etag = $"\"{Guid.NewGuid():n}\"";
context.Response.Headers.ETag = etag;
await Gravatar.WriteGravatar(context);
}).CacheOutput();
Cara lain untuk melakukan validasi ulang cache adalah dengan memeriksa tanggal pembuatan entri cache dibandingkan dengan tanggal yang diminta oleh klien. Saat header If-Modified-Since
permintaan disediakan, penembolokan output mengembalikan 304 jika entri yang di-cache lebih lama dan tidak kedaluwarsa.
Validasi ulang cache otomatis sebagai respons terhadap header ini yang dikirim dari klien. Tidak ada konfigurasi khusus yang diperlukan pada server untuk mengaktifkan perilaku ini, selain mengaktifkan penembolokan output.
Menggunakan tag untuk mengeluarkan entri cache
Anda dapat menggunakan tag untuk mengidentifikasi sekelompok titik akhir dan mengeluarkan semua entri cache untuk grup. Misalnya, kode berikut membuat sepasang titik akhir yang URL-nya dimulai dengan "blog", dan menandainya "tag-blog":
app.MapGet("/blog", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog"));
app.MapGet("/blog/post/{id}", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog"));
Cara alternatif untuk menetapkan tag untuk pasangan titik akhir yang sama adalah dengan menentukan kebijakan dasar yang berlaku untuk titik akhir yang dimulai dengan blog
:
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));
});
Alternatif lain adalah memanggil MapGroup
:
var blog = app.MapGroup("blog")
.CacheOutput(builder => builder.Tag("tag-blog"));
blog.MapGet("/", Gravatar.WriteGravatar);
blog.MapGet("/post/{id}", Gravatar.WriteGravatar);
Dalam contoh penetapan tag sebelumnya, kedua titik akhir diidentifikasi oleh tag-blog
tag. Anda kemudian dapat mengeluarkan entri cache untuk titik akhir tersebut dengan satu pernyataan yang mereferensikan tag tersebut:
app.MapPost("/purge/{tag}", async (IOutputCacheStore cache, string tag) =>
{
await cache.EvictByTagAsync(tag, default);
});
Dengan kode ini, permintaan HTTP POST yang dikirim akan https://localhost:<port>/purge/tag-blog
mengeluarkan entri cache untuk titik akhir ini.
Anda mungkin ingin cara untuk mengeluarkan semua entri cache untuk semua titik akhir. Untuk melakukannya, buat kebijakan dasar untuk semua titik akhir seperti yang dilakukan kode berikut:
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));
});
Kebijakan dasar ini memungkinkan Anda menggunakan tag "tag-all" untuk mengeluarkan semuanya dalam cache.
Menonaktifkan penguncian sumber daya
Secara default, penguncian sumber daya diaktifkan untuk mengurangi risiko cache stampede dan kawanan guntur. Untuk informasi selengkapnya, lihat Penembolokan Output.
Untuk menonaktifkan penguncian sumber daya, panggil SetLocking(false) saat membuat kebijakan, seperti yang ditunjukkan dalam contoh berikut:
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));
});
Contoh berikut memilih kebijakan tanpa penguncian untuk titik akhir:
app.MapGet("/nolock", Gravatar.WriteGravatar)
.CacheOutput("NoLock");
Batas
Properti OutputCacheOptions berikut memungkinkan Anda mengonfigurasi batas yang berlaku untuk semua titik akhir:
- SizeLimit - Ukuran maksimum penyimpanan cache. Ketika batas ini tercapai, tidak ada respons baru yang akan di-cache hingga entri yang lebih lama dikeluarkan. Nilai defaultnya adalah 100 MB.
- MaximumBodySize - Jika isi respons melebihi batas ini, itu tidak akan di-cache. Nilai defaultnya adalah 64 MB.
- DefaultExpirationTimeSpan - Durasi waktu kedaluwarsa yang berlaku ketika tidak ditentukan oleh kebijakan. Nilai defaultnya adalah 60 detik.
Penyimpanan cache
IOutputCacheStore digunakan untuk penyimpanan. Secara default digunakan dengan MemoryCache. Kami tidak merekomendasikan IDistributedCache untuk digunakan dengan penembolokan output. IDistributedCache
tidak memiliki fitur atomik, yang diperlukan untuk pemberian tag. Kami menyarankan agar Anda membuat implementasi kustom IOutputCacheStore dengan menggunakan dependensi langsung pada mekanisme penyimpanan yang mendasar, seperti Redis. Atau gunakan dukungan bawaan untuk cache Redis di .NET 8..
Lihat juga
ASP.NET Core