Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Door Tom Dykstra
Note
Dit is niet de nieuwste versie van dit artikel. Zie de .NET 10-versie van dit artikel voor de huidige release.
Warning
Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie het .NET- en .NET Core-ondersteuningsbeleid voor meer informatie. Zie de .NET 10-versie van dit artikel voor de huidige release.
In dit artikel wordt uitgelegd hoe u middleware voor uitvoercaching configureert in een ASP.NET Core-app. Zie Uitvoercaching voor een inleiding tot het opslaan van uitvoercache.
De middleware voor uitvoercache kan worden gebruikt in alle typen ASP.NET Core-apps: Minimale API's, Web-API met controllers, MVC en Razor Pages. Codevoorbeelden worden gegeven voor minimale API's en api's op basis van een controller. In de api-voorbeelden op basis van een controller ziet u hoe u kenmerken gebruikt om caching te configureren. Deze kenmerken kunnen ook worden gebruikt in MVC- en Razor Pages-apps.
De codevoorbeelden verwijzen naar een Gravatar-klasse die een afbeelding genereert en een 'gegenereerde op' datum en tijd biedt. De klasse wordt gedefinieerd en wordt alleen gebruikt in de voorbeeld-app. Het doel hiervan is om gemakkelijk te zien wanneer uitvoer in de cache wordt gebruikt. Voor meer informatie, zie Een voorbeeld downloaden en Preprocessor-instructies.
De middleware toevoegen aan de app
Voeg de middleware voor uitvoercaching toe aan de serviceverzameling door de AddOutputCache methode aan te roepen. Voorbeeld:
builder.Services.AddOutputCache();
Voeg de middleware toe aan de aanvraagverwerkingspijplijn door de methode aan te UseOutputCache roepen. Voorbeeld:
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseOutputCache();
Bij het aanroepen van de AddOutputCache en UseOutputCache methoden wordt het cachegedrag niet gestart, maar wordt caching beschikbaar gemaakt. Om ervoor te zorgen dat de app responsen in de cache opslaat, moet caching worden geconfigureerd zoals in de volgende secties beschreven.
Note
- In apps die gebruikmaken van CORS-middleware (Cross-Origin Requests), moet de
UseOutputCachemethode worden aangeroepen na de UseCors methode. - In Razor Pages-apps en -apps met controllers moet de
UseOutputCachemethode worden aangeroepen na deUseRoutingmethode. - In apps die gebruikmaken van verificatie of autorisatie, moet de
UseOutputCachemethode worden aangeroepen na de UseAuthentication en UseAuthorization methoden. Anders kan de uitvoercaching middleware inhoud in de cache leveren voor niet-geautoriseerde gebruikers in plaats van inhoud voor geautoriseerde gebruikers.
Eén eindpunt of pagina configureren
Voor minimale API-apps configureert u een eindpunt om caching uit te voeren door de CacheOutput-methode aan te roepen of door het kenmerk [OutputCache] toe te passen, zoals wordt weergegeven in de volgende voorbeelden:
app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
app.MapGet("/attribute", [OutputCache] (context) =>
Gravatar.WriteGravatar(context));
Voor apps met controllers past u het [OutputCache] kenmerk toe op de actiemethode, zoals wordt weergegeven in de volgende code:
[ApiController]
[Route("/[controller]")]
[OutputCache]
public class CachedController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Voor Razor Pagina-apps past u het kenmerk toe op de Razor paginaklasse.
Meerdere eindpunten of pagina's configureren
Maak beleidsregels bij het aanroepen van de methode om de AddOutputCache cacheconfiguratie op te geven die van toepassing is op meerdere eindpunten. Een beleid kan worden geselecteerd voor specifieke eindpunten, terwijl een basisbeleid standaardconfiguratie voor caching biedt voor een verzameling eindpunten.
Met de volgende gemarkeerde code wordt caching geconfigureerd voor alle eindpunten van de app, met een verlooptijd van 10 seconden. Als er geen verlooptijd is opgegeven, wordt deze standaard ingesteld op één minuut.
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)));
});
Met de volgende gemarkeerde code worden twee beleidsregels gemaakt, die elk een andere verlooptijd opgeven. Geselecteerde eindpunten kunnen de verlooptijd van 20 seconden gebruiken en anderen kunnen de verlooptijd van 30 seconden gebruiken.
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)));
});
U kunt een beleid voor een eindpunt selecteren bij het aanroepen van de CacheOutput methode of met behulp van het [OutputCache] kenmerk.
In een minimale API-app configureert de volgende code één eindpunt met een verlooptijd van 20 seconden en één met een verlooptijd van 30 seconden:
app.MapGet("/20", Gravatar.WriteGravatar).CacheOutput("Expire20");
app.MapGet("/30", [OutputCache(PolicyName = "Expire30")] (context) =>
Gravatar.WriteGravatar(context));
Voor apps met controllers past u het [OutputCache] kenmerk toe op de actiemethode om een beleid te selecteren:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "Expire20")]
public class Expire20Controller : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Voor Razor Pagina-apps past u het kenmerk toe op de Razor paginaklasse.
Werken met het standaardbeleid voor uitvoercache
Standaard volgt de uitvoercache de volgende regels:
- Alleen HTTP 200-antwoorden worden in de cache opgeslagen.
- Alleen HTTP GET- of HEAD-aanvragen worden in de cache opgeslagen.
- Antwoorden die cookies instellen, worden niet in de cache opgeslagen.
- Antwoorden op geverifieerde aanvragen worden niet in de cache opgeslagen.
Met de volgende code worden alle standaardregels voor caching toegepast op alle eindpunten van een app:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Cache());
});
Het standaardbeleid overschrijven
De volgende code laat zien hoe u de standaardbeleidsregels overschrijft. De gemarkeerde regels in de volgende aangepaste beleidscode maken caching mogelijk voor HTTP POST-methoden en HTTP 301-antwoorden:
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;
}
}
Als u dit aangepaste beleid wilt gebruiken, maakt u een benoemd beleid:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", MyCustomPolicy.Instance);
});
En selecteer het benoemde beleid voor een eindpunt. De volgende code selecteert het aangepaste beleid voor een eindpunt in een minimale API-app:
app.MapPost("/cachedpost", Gravatar.WriteGravatar)
.CacheOutput("CachePost");
De volgende code doet hetzelfde voor een controlleractie:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "CachePost")]
public class PostController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Een alternatieve overschrijving van het standaardbeleid gebruiken
U kunt ook afhankelijkheidsinjectie (DI) gebruiken om een exemplaar te initialiseren met de volgende wijzigingen in de aangepaste beleidsklasse:
- Gebruik een openbare constructor in plaats van een privéconstructor.
- Verwijder de
Instanceeigenschap in de aangepaste beleidsklasse.
Voorbeeld:
public sealed class MyCustomPolicy2 : IOutputCachePolicy
{
public MyCustomPolicy2()
{
}
De rest van de klasse is hetzelfde als eerder weergegeven. Voeg het aangepaste beleid toe, zoals wordt weergegeven in het volgende voorbeeld:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", builder =>
builder.AddPolicy<MyCustomPolicy2>(), true);
});
De voorgaande code maakt gebruik van DI om het exemplaar van de aangepaste beleidsklasse te maken. Alle publieke argumenten in de constructor worden opgelost.
Wanneer u een aangepast beleid als basisbeleid gebruikt, roept u de OutputCache() methode (zonder argumenten) niet aan of gebruikt u het [OutputCache] kenmerk op een eindpunt waarop het basisbeleid van toepassing moet zijn. Als u de OutputCache() methode aanroept of het kenmerk gebruikt, wordt het standaardbeleid aan het eindpunt toegevoegd.
De cachesleutel opgeven
Standaard wordt elk deel van de URL opgenomen als de sleutel voor een cachevermelding, dat wil gezegd, het schema, de host, de poort, het pad en de querytekenreeks. Het is echter mogelijk dat u de cachesleutel expliciet wilt beheren. Stel dat u een eindpunt hebt dat alleen een uniek antwoord retourneert voor elke unieke waarde van de culture querytekenreeks. Variatie in andere delen van de URL, zoals andere queryreeksen, mag niet resulteren in verschillende cachevermeldingen. U kunt dergelijke regels opgeven in een beleid, zoals wordt weergegeven in de volgende gemarkeerde code:
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));
});
Vervolgens kunt u het VaryByQuery beleid voor een eindpunt selecteren. In een minimale API-app selecteert de volgende code het VaryByQuery beleid voor een eindpunt dat alleen een uniek antwoord retourneert voor elke unieke waarde van de culture queryreeks:
app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput("Query");
De volgende code doet hetzelfde voor een controlleractie:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "Query")]
public class QueryController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Hier volgen enkele van de opties voor het beheren van de cachesleutel:
De SetVaryByQuery methode geeft een of meer namen van queryreeksen op die moeten worden toegevoegd aan de cachesleutel.
De SetVaryByHeader methode geeft een of meer HTTP-headers op die moeten worden toegevoegd aan de cachesleutel.
De VaryByValue methode biedt een waarde die moet worden toegevoegd aan de cachesleutel. In het volgende voorbeeld wordt een waarde gebruikt die aangeeft of de huidige servertijd in seconden oneven of zelfs is. Er wordt alleen een nieuw antwoord gegenereerd wanneer het aantal seconden verandert van een oneven naar even waarde, of omgekeerd.
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)))); });
Gebruik de OutputCacheOptions.UseCaseSensitivePaths eigenschap om aan te geven dat het padgedeelte van de sleutel hoofdlettergevoelig is. De standaardwaarde is niet hoofdlettergevoelig.
Zie de OutputCachePolicyBuilder klasse voor meer opties.
Cachevalidatie inschakelen
Cachevalidatie betekent dat de server een HTTP-statuscode van 304 niet gewijzigd kan retourneren in plaats van de volledige hoofdtekst van het antwoord. Deze statuscode informeert de client dat het antwoord op de aanvraag ongewijzigd blijft ten opzichte van wat de client eerder heeft ontvangen.
De volgende code illustreert het gebruik van een ETag-header voor het opnieuw valideren van de cache. Als de client een If-None-Match-header verzendt met de waarde voor het ETag eerdere antwoord en de cachevermelding nieuw is, retourneert de server de code 304 Niet gewijzigd in plaats van het volledige antwoord.
Met de volgende code wordt de ETag waarde in een beleid in een minimale API-app ingesteld:
app.MapGet("/etag", async (context) =>
{
var etag = $"\"{Guid.NewGuid():n}\"";
context.Response.Headers.ETag = etag;
await Gravatar.WriteGravatar(context);
}).CacheOutput();
De volgende code doet hetzelfde voor een API op basis van een controller:
[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);
}
}
Een andere manier om cachehervalidatie uit te voeren, is door de datum te controleren waarop de cachevermelding is gemaakt in vergelijking met de datum die door de client is aangevraagd. Wanneer de aanvraagheader If-Modified-Since is opgegeven, retourneert de uitvoercache de 304-code als de vermelding in de cache ouder is en niet is verlopen.
De cachevalidatie wordt automatisch uitgevoerd als reactie op deze headers die vanaf de client worden verzonden. Er is geen speciale configuratie vereist op de server om dit gedrag in te schakelen, afgezien van het inschakelen van uitvoercache.
Tags gebruiken om cachevermeldingen te verwijderen
U kunt tags gebruiken om een groep eindpunten te identificeren en alle cachevermeldingen voor de groep te verwijderen. Met de volgende minimale API-code wordt bijvoorbeeld een paar eindpunten gemaakt waarvan de URL's beginnen met de tekst blog en de tag-blog tag toepassen:
app.MapGet("/blog", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog"));
app.MapGet("/blog/post/{id}", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog"));
De volgende code laat zien hoe u tags toewijst aan een eindpunt in een controller-API:
[ApiController]
[Route("/[controller]")]
[OutputCache(Tags = new[] { "tag-blog", "tag-all" })]
public class TagEndpointController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Een alternatieve manier om tags toe te wijzen voor eindpunten met routes die beginnen blog , is door een basisbeleid te definiëren dat van toepassing is op alle eindpunten met die route. De volgende code demonstreert deze benadering:
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));
});
Een ander alternatief voor Minimale API-apps is het aanroepen van de MapGroup-methode :
var blog = app.MapGroup("blog")
.CacheOutput(builder => builder.Tag("tag-blog"));
blog.MapGet("/", Gravatar.WriteGravatar);
blog.MapGet("/post/{id}", Gravatar.WriteGravatar);
In de voorgaande voorbeelden van tagtoewijzing worden beide eindpunten geïdentificeerd door de tag-blog tag. Vervolgens kunt u de cachevermeldingen voor deze eindpunten verwijderen met één instructie die verwijst naar die tag:
app.MapPost("/purge/{tag}", async (IOutputCacheStore cache, string tag) =>
{
await cache.EvictByTagAsync(tag, default);
});
Met deze code wordt een HTTP POST-aanvraag die naar de https://localhost:<port>/purge/tag-blog URL wordt verzonden, cachevermeldingen voor deze eindpunten verwijderd.
Mogelijk wilt u alle cachevermeldingen voor alle eindpunten verwijderen. U kunt een basisbeleid maken voor alle eindpunten, zoals wordt gedemonstreerd in de volgende code:
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));
});
Met dit basisbeleid kunt u de tag gebruiken om alles in de tag-all cache te verwijderen.
Resourcevergrendeling uitschakelen
Resourcevergrendeling is standaard ingeschakeld om het risico van cache-stormloop en donderende kudde te beperken. Zie Uitvoercache voor meer informatie.
Als u resourcevergrendeling wilt uitschakelen, roept u de methode SetLocking(false) aan tijdens het maken van een beleid, zoals wordt weergegeven in het volgende voorbeeld:
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));
});
In het volgende voorbeeld wordt het beleid zonder vergrendeling voor een eindpunt in een minimale API-app geselecteerd:
app.MapGet("/nolock", Gravatar.WriteGravatar)
.CacheOutput("NoLock");
Gebruik in een api op basis van een controller het kenmerk om het beleid te selecteren:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "NoLock")]
public class NoLockController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Limieten configureren
Met de volgende eigenschappen van de OutputCacheOptions klasse kunt u limieten configureren die van toepassing zijn op alle eindpunten:
- Met SizeLimit de eigenschap wordt de maximale grootte voor de cacheopslag ingesteld. Wanneer de limiet is bereikt, worden er geen nieuwe antwoorden in de cache opgeslagen totdat oudere vermeldingen worden verwijderd. De standaardwaarde is 100 MB.
- De MaximumBodySize eigenschap stelt de maximale grootte in voor de hoofdtekst van het antwoord. Als de hoofdtekst van het antwoord de limiet overschrijdt, wordt deze niet in de cache opgeslagen. De standaardwaarde is 64 MB.
- Met DefaultExpirationTimeSpan de eigenschap wordt de maximale duur ingesteld waarop een antwoord in de cache wordt opgeslagen, wanneer een tijd niet is opgegeven in een beleid. De standaardwaarde is 60 seconden.
Opties voor cacheopslag verkennen
De IOutputCacheStore interface wordt gebruikt voor opslag. Deze wordt standaard gebruikt met de MemoryCache klasse. Reacties in de cache worden in het proces opgeslagen, dus elke server heeft een afzonderlijke cache die verloren gaat wanneer het serverproces opnieuw wordt opgestart.
Alternatief: Redis cache
Een alternatief is het gebruik van Redis Cache. Redis-cache biedt consistentie tussen serverknooppunten via een gedeelde cache die afzonderlijke serverprocessen overleeft. Redis gebruiken voor uitvoercache:
Installeer het NuGet-pakket Microsoft.AspNetCore.OutputCaching.StackExchangeRedis .
Roep de methode
builder.Services.AddStackExchangeRedisOutputCacheaan (niet de methodeAddStackExchangeRedisCache) en geef een verbindingsreeks op die verwijst naar een Redis-server.Voorbeeld:
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))); });De eigenschap options.Configuration is een verbindingsreeks naar een on-premises Redis-server of naar een gehoste oplossing zoals Azure Cache voor Redis. Bijvoorbeeld
<instance_name>.redis.cache.windows.net:6380,password=,pw,ssl=True,abortConnect=Falsevoor Azure Cache voor Redis.(Optioneel) De opties.InstanceName eigenschap specificeert een logische partitie voor de cache.
De configuratieopties zijn identiek aan de opties voor gedistribueerde caching op basis van Redis.
Niet aanbevolen: IDistributedCache
De IDistributedCache interface wordt niet aanbevolen voor gebruik met uitvoercache. Deze interface biedt geen atomische functies, die vereist zijn voor taggen.
De aanbevolen benadering is het gebruik van de ingebouwde ondersteuning voor Redis of het maken van een aangepaste IOutputCacheStore implementatie met behulp van directe afhankelijkheden van het onderliggende opslagmechanisme.
Verwante inhoud
In dit artikel wordt uitgelegd hoe u middleware voor uitvoercaching configureert in een ASP.NET Core-app. Zie Uitvoercaching voor een inleiding tot het opslaan van uitvoercache.
De uitvoercaching-middleware kan worden gebruikt in alle typen ASP.NET Core-apps: Minimale API, Web-API met controllers, MVC en Razor Pages. De voorbeeld-app is een minimale API, maar elke cachefunctie die wordt geïllustreerd, wordt ook ondersteund in de andere app-typen.
De middleware toevoegen aan de app
Voeg de middleware voor uitvoercaching toe aan de serviceverzameling door aan te roepen AddOutputCache.
Voeg de middleware toe aan de aanvraagverwerkingspijplijn door aan te roepen UseOutputCache.
Note
- In apps die gebruikmaken van CORS middleware,
UseOutputCachemoet hierna UseCorsworden aangeroepen. - In Razor Pagina-apps en apps met controllers, moet
UseOutputCachenaUseRoutingworden aangeroepen. - In apps die gebruikmaken van verificatie of autorisatie,
UseOutputCachemoeten worden aangeroepen na UseAuthentication en UseAuthorization. Anders kan de uitvoercaching middleware inhoud in de cache leveren voor niet-geautoriseerde gebruikers in plaats van inhoud voor geautoriseerde gebruikers. - Het bellen van
AddOutputCacheenUseOutputCacheactiveert het cachegedrag niet, maar maakt caching beschikbaar. Cache antwoordgegevens moeten worden geconfigureerd zoals in de volgende secties wordt weergegeven.
Eén eindpunt of pagina configureren
Voor minimale API-apps configureert u een eindpunt voor caching door aan te roepen CacheOutputof door het [OutputCache] kenmerk toe te passen, zoals wordt weergegeven in de volgende voorbeelden:
app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
app.MapGet("/attribute", [OutputCache] (context) =>
Gravatar.WriteGravatar(context));
Voor apps met controllers past u het kenmerk [OutputCache] toe op de actiemethode. Voor Razor Pagina-apps past u het kenmerk toe op de Razor paginaklasse.
Meerdere eindpunten of pagina's configureren
Maak beleidsregels bij het aanroepen AddOutputCache om de cacheconfiguratie op te geven die van toepassing is op meerdere eindpunten. Een beleid kan worden geselecteerd voor specifieke eindpunten, terwijl een basisbeleid standaardconfiguratie voor caching biedt voor een verzameling eindpunten.
Met de volgende gemarkeerde code wordt caching geconfigureerd voor alle eindpunten van de app, met een verlooptijd van 10 seconden. Als er geen verlooptijd is opgegeven, wordt deze standaard ingesteld op één minuut.
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)));
});
Met de volgende gemarkeerde code worden twee beleidsregels gemaakt, die elk een andere verlooptijd opgeven. Geselecteerde eindpunten kunnen de verlooptijd van 20 seconden gebruiken en anderen kunnen de verlooptijd van 30 seconden gebruiken.
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)));
});
U kunt een beleid voor een eindpunt selecteren wanneer u de CacheOutput methode aanroept of het [OutputCache] kenmerk gebruikt:
app.MapGet("/20", Gravatar.WriteGravatar).CacheOutput("Expire20");
app.MapGet("/30", [OutputCache(PolicyName = "Expire30")] (context) =>
Gravatar.WriteGravatar(context));
Voor apps met controllers past u het kenmerk [OutputCache] toe op de actiemethode. Voor Razor Pagina-apps past u het kenmerk toe op de Razor paginaklasse.
Standaardbeleid voor uitvoercache
Standaard volgt de uitvoercache de volgende regels:
- Alleen HTTP 200-antwoorden worden in de cache opgeslagen.
- Alleen HTTP GET- of HEAD-aanvragen worden in de cache opgeslagen.
- Antwoorden die cookies instellen, worden niet in de cache opgeslagen.
- Antwoorden op geverifieerde aanvragen worden niet in de cache opgeslagen.
Met de volgende code worden alle standaardregels voor caching toegepast op alle eindpunten van een app:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Cache());
});
Het standaardbeleid overschrijven
De volgende code laat zien hoe u de standaardregels overschrijft. De gemarkeerde regels in de volgende aangepaste beleidscode maken caching mogelijk voor HTTP POST-methoden en HTTP 301-antwoorden:
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;
}
}
Als u dit aangepaste beleid wilt gebruiken, maakt u een benoemd beleid:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", MyCustomPolicy.Instance);
});
En selecteer het benoemde beleid voor een eindpunt:
app.MapPost("/cachedpost", Gravatar.WriteGravatar)
.CacheOutput("CachePost");
Alternatieve overschrijving van het standaardbeleid
U kunt ook afhankelijkheidsinjectie (DI) gebruiken om een instantie te initialiseren, met de volgende wijzigingen in de klasse van het aangepaste beleid:
- Een openbare constructor in plaats van een privéconstructor.
- Verwijder de
Instanceeigenschap in de aangepaste beleidsklasse.
Voorbeeld:
public sealed class MyCustomPolicy2 : IOutputCachePolicy
{
public MyCustomPolicy2()
{
}
De rest van de klasse is hetzelfde als eerder weergegeven. Voeg het aangepaste beleid toe, zoals wordt weergegeven in het volgende voorbeeld:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", builder =>
builder.AddPolicy<MyCustomPolicy2>(), true);
});
De voorgaande code maakt gebruik van DI om het exemplaar van de aangepaste beleidsklasse te maken. Alle publieke argumenten in de constructor worden opgelost.
Wanneer u een aangepast beleid als basisbeleid gebruikt, roept u geen aanroep OutputCache() (zonder argumenten) op een eindpunt waarop het basisbeleid van toepassing moet zijn. Aanroepen OutputCache() voegt het standaardbeleid toe aan het eindpunt.
De cachesleutel opgeven
Standaard wordt elk deel van de URL opgenomen als de sleutel voor een cachevermelding, dat wil gezegd, het schema, de host, de poort, het pad en de querytekenreeks. Het is echter mogelijk dat u de cachesleutel expliciet wilt beheren. Stel dat u een eindpunt hebt dat alleen een uniek antwoord retourneert voor elke unieke waarde van de culture querytekenreeks. Variatie in andere delen van de URL, zoals andere queryreeksen, mag niet resulteren in verschillende cachevermeldingen. U kunt dergelijke regels opgeven in een beleid, zoals wordt weergegeven in de volgende gemarkeerde code:
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));
});
Vervolgens kunt u het VaryByQuery beleid voor een eindpunt selecteren:
app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput("Query");
Hier volgen enkele van de opties voor het beheren van de cachesleutel:
SetVaryByQuery - Geef een of meer namen van queryreeksen op die u wilt toevoegen aan de cachesleutel.
SetVaryByHeader - Geef een of meer HTTP-headers op die u wilt toevoegen aan de cachesleutel.
VaryByValue- Geef een waarde op die moet worden toegevoegd aan de cachesleutel. In het volgende voorbeeld wordt een waarde gebruikt die aangeeft of de huidige servertijd in seconden oneven of zelfs is. Er wordt alleen een nieuw antwoord gegenereerd wanneer het aantal seconden van oneven naar even of zelfs oneven gaat.
app.MapGet("/varybyvalue", Gravatar.WriteGravatar) .CacheOutput(c => c.VaryByValue((context) => new KeyValuePair<string, string>( "time", (DateTime.Now.Second % 2) .ToString(CultureInfo.InvariantCulture))));
Gebruik OutputCacheOptions.UseCaseSensitivePaths om op te geven dat het padgedeelte van de sleutel hoofdlettergevoelig is. De standaardwaarde is niet hoofdlettergevoelig.
Zie de OutputCachePolicyBuilder klasse voor meer opties.
Cache-hernieuwde-validatie
Cachevalidatie betekent dat de server een 304 Not Modified HTTP-statuscode kan retourneren in plaats van de volledige hoofdtekst van het antwoord. Deze statuscode informeert de client dat het antwoord op de aanvraag ongewijzigd blijft ten opzichte van wat de client eerder heeft ontvangen.
De volgende code illustreert het gebruik van een Etag header om opnieuw valideren van de cache in te schakelen. Als de client een If-None-Match header met de etag-waarde van een eerder antwoord verzendt en de cachevermelding nieuw is, retourneert de server 304 Niet gewijzigd in plaats van het volledige antwoord:
app.MapGet("/etag", async (context) =>
{
var etag = $"\"{Guid.NewGuid():n}\"";
context.Response.Headers.ETag = etag;
await Gravatar.WriteGravatar(context);
}).CacheOutput();
Een andere manier om cachehervalidatie uit te voeren, is door de datum te controleren waarop de cachevermelding is gemaakt in vergelijking met de datum die door de client is aangevraagd. Wanneer de aanvraagheader If-Modified-Since wordt opgegeven, retourneert de uitvoercache 304 als de vermelding in de cache ouder is en niet is verlopen.
De cachevalidatie wordt automatisch uitgevoerd als reactie op deze headers die vanaf de client worden verzonden. Er is geen speciale configuratie vereist op de server om dit gedrag in te schakelen, afgezien van het inschakelen van uitvoercache.
Tags gebruiken om cachevermeldingen te verwijderen
U kunt tags gebruiken om een groep eindpunten te identificeren en alle cachevermeldingen voor de groep te verwijderen. Met de volgende code maakt u bijvoorbeeld een paar eindpunten waarvan de URL's beginnen met 'blog' en tags '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"));
Een alternatieve manier om tags toe te wijzen voor hetzelfde paar eindpunten is door een basisbeleid te definiëren dat van toepassing is op eindpunten die beginnen met 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));
});
Een ander alternatief is om MapGroup te bellen:
var blog = app.MapGroup("blog")
.CacheOutput(builder => builder.Tag("tag-blog"));
blog.MapGet("/", Gravatar.WriteGravatar);
blog.MapGet("/post/{id}", Gravatar.WriteGravatar);
In de voorgaande voorbeelden van tagtoewijzing worden beide eindpunten geïdentificeerd door de tag-blog tag. Vervolgens kunt u de cachevermeldingen voor deze eindpunten verwijderen met één instructie die verwijst naar die tag:
app.MapPost("/purge/{tag}", async (IOutputCacheStore cache, string tag) =>
{
await cache.EvictByTagAsync(tag, default);
});
Met deze code verwijdert een HTTP POST-aanvraag die naar https://localhost:<port>/purge/tag-blog wordt verzonden cachevermeldingen voor deze eindpunten.
Mogelijk wilt u alle cachevermeldingen voor alle eindpunten verwijderen. Hiervoor maakt u een basisbeleid voor alle eindpunten, zoals in de volgende code:
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));
});
Met dit basisbeleid kunt u de tag 'tag-all' gebruiken om alles in de cache te verwijderen.
Resourcevergrendeling uitschakelen
Resourcevergrendeling is standaard ingeschakeld om het risico van cache-stormloop en donderende kudde te beperken. Zie Uitvoercache voor meer informatie.
Als u resourcevergrendeling wilt uitschakelen, roept u SetLocking(false) aan tijdens het maken van een beleid, zoals wordt weergegeven in het volgende voorbeeld:
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));
});
In het volgende voorbeeld wordt het beleid voor geen vergrendeling voor een eindpunt geselecteerd:
app.MapGet("/nolock", Gravatar.WriteGravatar)
.CacheOutput("NoLock");
Limits
Met de volgende eigenschappen OutputCacheOptions kunt u limieten configureren die van toepassing zijn op alle eindpunten:
- SizeLimit - Maximale grootte van cacheopslag. Wanneer deze limiet is bereikt, worden er geen nieuwe antwoorden in de cache opgeslagen totdat oudere vermeldingen worden verwijderd. De standaardwaarde is 100 MB.
- MaximumBodySize - Als de hoofdtekst van het antwoord deze limiet overschrijdt, wordt deze niet in de cache opgeslagen. De standaardwaarde is 64 MB.
- DefaultExpirationTimeSpan - De verlooptijd die van toepassing is wanneer deze niet is opgegeven door een beleid. De standaardwaarde is 60 seconden.
Cacheopslag
IOutputCacheStore wordt gebruikt voor opslag. Standaard wordt deze gebruikt met MemoryCache. We raden u niet aan IDistributedCache voor gebruik met uitvoercache.
IDistributedCache heeft geen atomische functies, die vereist zijn voor taggen. U wordt aangeraden aangepaste IOutputCacheStore implementaties te maken met behulp van directe afhankelijkheden van het onderliggende opslagmechanisme, zoals Redis. Of gebruik de ingebouwde ondersteuning voor Redis Cache in .NET 8..