Delen via


Middleware voor outputcaching in ASP.NET Core

Door Tom Dykstra

Note

Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-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 9-versie van dit artikel voor de huidige release.

Important

Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.

Zie de .NET 9-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 uitvoercaching-middleware kan worden gebruikt in alle typen ASP.NET Core-apps: Minimale API, Web-API met controllers, MVC en Razor Pages. Codevoorbeelden worden gegeven voor minimale API's en op controller gebaseerde API's. 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. Zie voor meer informatie Hoe u een voorbeeld kunt downloaden en Preprocessor-instructies in voorbeeldcode.

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.

Voorbeeld:

builder.Services.AddOutputCache();
var app = builder.Build();

// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseOutputCache();
app.UseAuthorization();

Het bellen van AddOutputCache en UseOutputCache activeert het cachegedrag niet, maar maakt caching beschikbaar. Om ervoor te zorgen dat de app reacties in de cache opslaat, moet caching worden geconfigureerd zoals in de volgende secties wordt weergegeven.

Note

  • In apps die gebruikmaken van CORS middleware, UseOutputCache moet hierna UseCorsworden aangeroepen.
  • In Razor Pagina-apps en apps met controllers, moet UseOutputCache na UseRouting worden aangeroepen.

Eén eindpunt of pagina configureren

Voor minimale API-apps configureert u een eindpunt om caching uit te voeren 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 [OutputCache] kenmerk toe op de actiemethode, zoals hier wordt weergegeven:

[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 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.

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.

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. 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);
    }
}

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 Instance eigenschap 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 niet OutputCache() aan (zonder argumenten) of gebruikt u het [OutputCache] kenmerk op een eindpunt waarop het basisbeleid van toepassing moet zijn. Als u het kenmerk aanroept OutputCache() of gebruikt, wordt het standaardbeleid toegevoegd 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. 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:

  • 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.

    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 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.

Cachevalidatie

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. U kunt als volgt de etag-waarde instellen in een beleid in een minimale API-app:

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

}).CacheOutput();

En u kunt als volgt de etag-waarde instellen in 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 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 minimale API-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"));

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. In de volgende code ziet u hoe u dit doet:

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 MapGroupvan:

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-verzoek verzonden om de cache-items voor deze eindpunten te verwijderen via https://localhost:<port>/purge/tag-blog.

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 zonder vergrendeling voor een eindpunt geselecteerd in een minimale API-app:

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);
    }
}

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. 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.

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 builder.Services.AddStackExchangeRedisOutputCache (niet AddStackExchangeRedisCache) aan 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)));
    });
    
    • options.Configuration - Een verbindingsreeks naar een on-premises Redis-server of een gehoste aanbieding zoals Azure Cache voor Redis. Bijvoorbeeld <instance_name>.redis.cache.windows.net:6380,password=,pw,ssl=True,abortConnect=False voor Azure Cache voor Redis.
    • options.InstanceName - Optioneel, geeft een logische partitie voor de cache.

    De configuratieopties zijn identiek aan de opties voor gedistribueerde caching op basis van Redis.

We raden u niet aan IDistributedCache voor gebruik met uitvoercache. IDistributedCache heeft geen atomische functies, die vereist zijn voor taggen. U wordt aangeraden de ingebouwde ondersteuning voor Redis te gebruiken of aangepaste IOutputCacheStore implementaties te maken met behulp van directe afhankelijkheden van het onderliggende opslagmechanisme.

Zie ook

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, UseOutputCache moet hierna UseCorsworden aangeroepen.
  • In Razor Pagina-apps en apps met controllers, moet UseOutputCache na UseRouting worden aangeroepen.
  • Het bellen van AddOutputCache en UseOutputCache activeert 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 om caching uit te voeren 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 Instance eigenschap 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.

Cachevalidatie

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..

Zie ook