Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Note
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Warning
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Questo articolo illustra come configurare il middleware di caching di output in un'app ASP.NET Core. Per un'introduzione alla memorizzazione nella cache dell'output, vedere Memorizzazione nella cache dell'output.
Il middleware di memorizzazione nella cache di output può essere usato in tutti i tipi di app di ASP.NET Core: APIminimal API Web con controller, MVC e Razor Pages. Vengono forniti esempi di codice per LE API minime e le API basate su controller. Gli esempi di API basate su controller illustrano come usare gli attributi per configurare la memorizzazione nella cache. Questi attributi possono essere usati anche nelle app MVC e Razor Pages.
Gli esempi di codice fanno riferimento a una classe Gravatar che genera un'immagine e fornisce data e ora "generate alle". La classe è definita e usata solo nell'app di esempio. Lo scopo è quello di semplificare la verifica quando viene usato l'output memorizzato nella cache. Per altre informazioni, vedere Come scaricare un esempio e direttive del preprocessore.
Aggiungere il middleware all'app
Aggiungere il middleware di memorizzazione nella cache di output alla raccolta di servizi chiamando il metodo AddOutputCache. Per esempio:
builder.Services.AddOutputCache();
Aggiungere il middleware alla pipeline di elaborazione delle richieste chiamando il UseOutputCache metodo . Per esempio:
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseOutputCache();
La chiamata ai metodi AddOutputCache e UseOutputCache non avvia il caching, ma lo rende disponibile. Per fare in modo che l'app memorizzi nella cache le risposte, la cache deve essere configurata come descritto nelle sezioni seguenti.
Note
- Nelle app che usano il middleware CORS (Cross-Origin Requests), il
UseOutputCachemetodo deve essere chiamato dopo il UseCors metodo . - Nelle app Razor Pages e nelle app con controller, il metodo
UseOutputCachedeve essere chiamato dopo il metodoUseRouting. - Nelle app che usano l'autenticazione o l'autorizzazione, il
UseOutputCachemetodo deve essere chiamato dopo i UseAuthentication metodi e UseAuthorization . In caso contrario, il middleware per la memorizzazione nella cache dell'output può fornire contenuto memorizzato nella cache destinato agli utenti non autorizzati invece del contenuto destinato agli utenti autorizzati.
Configurare un endpoint o una pagina
Per le app per le API minime, configurare un endpoint per eseguire la memorizzazione nella cache chiamando il metodo CacheOutput o applicando l'attributo [OutputCache], come illustrato negli esempi seguenti:
app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
app.MapGet("/attribute", [OutputCache] (context) =>
Gravatar.WriteGravatar(context));
Per le app con controller, applicare l'attributo [OutputCache] al metodo di azione come illustrato nel codice seguente:
[ApiController]
[Route("/[controller]")]
[OutputCache]
public class CachedController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Per Razor le app Pages, applicare l'attributo alla Razor classe della pagina.
Configurare più endpoint o pagine
Creare politiche quando si chiama il AddOutputCache metodo per specificare la configurazione di memorizzazione nella cache applicabile a più endpoint. È possibile selezionare un criterio per endpoint specifici, mentre un criterio di base fornisce la configurazione di memorizzazione nella cache predefinita per una raccolta di endpoint.
Il codice evidenziato seguente configura la memorizzazione nella cache per tutti gli endpoint dell'app, con una scadenza di 10 secondi. Se non viene specificata un'ora di scadenza, l'impostazione predefinita è un minuto.
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)));
});
Il codice evidenziato seguente crea due criteri, ognuno che specifica un'ora di scadenza diversa. Gli endpoint selezionati possono usare la scadenza di 20 secondi e altri possono usare la scadenza di 30 secondi.
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)));
});
È possibile selezionare un criterio per un endpoint quando si chiama il CacheOutput metodo o usando l'attributo [OutputCache] .
In un'app per le API minima, il codice seguente configura un endpoint con scadenza di 20 secondi e uno con scadenza di 30 secondi:
app.MapGet("/20", Gravatar.WriteGravatar).CacheOutput("Expire20");
app.MapGet("/30", [OutputCache(PolicyName = "Expire30")] (context) =>
Gravatar.WriteGravatar(context));
Per le app con controller, applicare l'attributo [OutputCache] al metodo di azione per selezionare un criterio:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "Expire20")]
public class Expire20Controller : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Per Razor le app Pages, applicare l'attributo alla Razor classe della pagina.
Usare la politica di caching dell'output predefinita
Per impostazione predefinita, la memorizzazione nella cache dell'output segue queste regole:
- Vengono memorizzate nella cache solo le risposte HTTP 200.
- Vengono memorizzate nella cache solo le richieste HTTP GET o HEAD.
- Le risposte che impostano i cookie non vengono memorizzate nella cache.
- Le risposte alle richieste autenticate non vengono memorizzate nella cache.
Il codice seguente applica tutte le regole di memorizzazione nella cache predefinite a tutti gli endpoint di un'app:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Cache());
});
Sovrascrivere i criteri predefiniti
Il codice seguente illustra come eseguire l'override delle regole dei criteri predefinite. Le righe evidenziate nel codice dei criteri personalizzati seguente abilitano la memorizzazione nella cache per i metodi HTTP POST e le risposte 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;
}
}
Per usare questo criterio personalizzato, creare un criterio denominato:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", MyCustomPolicy.Instance);
});
Selezionare quindi i criteri denominati per un endpoint. Il codice seguente seleziona la politica personalizzata per un endpoint in un'applicazione con Minimal API.
app.MapPost("/cachedpost", Gravatar.WriteGravatar)
.CacheOutput("CachePost");
Il codice seguente esegue la stessa operazione per un'azione del controller:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "CachePost")]
public class PostController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Usare un override alternativo dei criteri predefiniti
In alternativa, usare l'iniezione delle dipendenze (DI) per inizializzare un'istanza con le modifiche seguenti alla classe di criteri personalizzati:
- Usare un costruttore pubblico anziché un costruttore privato.
- Eliminare la
Instanceproprietà nella classe di criteri personalizzata.
Per esempio:
public sealed class MyCustomPolicy2 : IOutputCachePolicy
{
public MyCustomPolicy2()
{
}
Il resto della classe è uguale a quello illustrato in precedenza. Aggiungere i criteri personalizzati come illustrato nell'esempio seguente:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", builder =>
builder.AddPolicy<MyCustomPolicy2>(), true);
});
Il codice precedente usa DI per creare l'istanza della classe di criteri personalizzata. Tutti gli argomenti pubblici nel costruttore vengono risolti.
Quando si usa un criterio personalizzato come criterio di base, non chiamare il OutputCache() metodo (senza argomenti) o usare l'attributo [OutputCache] in qualsiasi endpoint a cui applicare i criteri di base. La chiamata al metodo o l'uso OutputCache() dell'attributo aggiunge i criteri predefiniti all'endpoint.
Specificare la chiave della cache
Per impostazione predefinita, ogni parte dell'URL viene inclusa come chiave di una voce della cache, ovvero lo schema, l'host, la porta, il percorso e la stringa di query. Tuttavia, potrebbe essere necessario controllare in modo esplicito la chiave della cache. Si supponga, ad esempio, di avere un endpoint che restituisce una risposta univoca solo per ogni valore univoco della culture stringa di query. La variazione in altre parti dell'URL, ad esempio altre stringhe di query, non dovrebbe comportare voci di cache diverse. È possibile specificare tali regole in un criterio, come illustrato nel codice evidenziato seguente:
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));
});
È quindi possibile selezionare i VaryByQuery criteri per un endpoint. In un'app per le API minima, il codice seguente seleziona i VaryByQuery criteri per un endpoint che restituisce una risposta univoca solo per ogni valore univoco della culture stringa di query:
app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput("Query");
Il codice seguente esegue la stessa operazione per un'azione del controller:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "Query")]
public class QueryController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Ecco alcune delle opzioni per controllare la chiave della cache:
Il SetVaryByQuery metodo specifica uno o più nomi di stringa di query da aggiungere alla chiave della cache.
Il SetVaryByHeader metodo specifica una o più intestazioni HTTP da aggiungere alla chiave della cache.
Il VaryByValue metodo fornisce un valore da aggiungere alla chiave della cache. Nell'esempio seguente viene utilizzato un valore che indica se il tempo del server corrente in secondi è dispari o pari. Viene generata una nuova risposta solo quando il numero di secondi cambia da un valore dispari a un valore pari o viceversa.
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)))); });
Utilizzare la OutputCacheOptions.UseCaseSensitivePaths proprietà per specificare che la parte del percorso della chiave fa distinzione tra maiuscole e minuscole. Il valore predefinito non fa distinzione tra maiuscole e minuscole.
Per altre opzioni, vedere la OutputCachePolicyBuilder classe .
Abilitare la riconvalida della cache
Riconvalida della cache indica che il server può restituire un codice di stato HTTP 304 non modificato anziché il corpo completo della risposta. Questo codice di stato informa il client che la risposta alla richiesta è invariata rispetto a quella ricevuta in precedenza dal client.
Il codice seguente illustra l'uso di un'intestazione ETag per abilitare la riconvalida della cache. Se il client invia un'intestazione If-None-Match con il valore di ETag da una risposta precedente e la voce della cache è aggiornata, il server restituisce il codice 304 Non modificato anziché la risposta completa.
Il codice seguente imposta il valore ETag in una policy in un'app Minimal API:
app.MapGet("/etag", async (context) =>
{
var etag = $"\"{Guid.NewGuid():n}\"";
context.Response.Headers.ETag = etag;
await Gravatar.WriteGravatar(context);
}).CacheOutput();
Il codice seguente esegue la stessa operazione per un'API basata su 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);
}
}
Un altro modo per eseguire la riconvalida della cache consiste nel controllare la data di creazione dell'elemento della cache rispetto alla data richiesta dal cliente. Quando viene specificata l'intestazione If-Modified-Since della richiesta, la memorizzazione nella cache dell'output restituisce il codice 304 se la voce memorizzata nella cache è precedente e non è scaduta.
La riconvalida della cache è automatica in risposta a queste intestazioni inviate dal client. Non è necessaria alcuna configurazione speciale nel server per abilitare questo comportamento, oltre ad abilitare la memorizzazione nella cache dell'output.
Usare i tag per eliminare le voci della cache
È possibile usare i tag per identificare un gruppo di endpoint ed eliminare tutte le voci della cache del gruppo. Ad esempio, il codice API minimo seguente crea una coppia di endpoint i cui URL iniziano con il testo blog e applica il tag-blog tag:
app.MapGet("/blog", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog"));
app.MapGet("/blog/post/{id}", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog"));
Il codice seguente illustra come assegnare tag a un endpoint in un'API basata su controller:
[ApiController]
[Route("/[controller]")]
[OutputCache(Tags = new[] { "tag-blog", "tag-all" })]
public class TagEndpointController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Un modo alternativo per assegnare tag per gli endpoint con route che iniziano con blog consiste nel definire un criterio di base applicabile a tutti gli endpoint con tale route. Il codice seguente illustra questo approccio:
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));
});
Un'altra alternativa per le app per le API minime consiste nel chiamare il metodo MapGroup :
var blog = app.MapGroup("blog")
.CacheOutput(builder => builder.Tag("tag-blog"));
blog.MapGet("/", Gravatar.WriteGravatar);
blog.MapGet("/post/{id}", Gravatar.WriteGravatar);
Negli esempi di assegnazione di tag precedenti, entrambi gli endpoint vengono identificati dal tag-blog tag . È quindi possibile eliminare le voci della cache per tali endpoint con una singola istruzione che fa riferimento a tale tag.
app.MapPost("/purge/{tag}", async (IOutputCacheStore cache, string tag) =>
{
await cache.EvictByTagAsync(tag, default);
});
Con questo codice, una richiesta HTTP POST inviata all'URL https://localhost:<port>/purge/tag-blog rimuove le voci della cache per questi endpoint.
Potresti voler rimuovere tutte le voci della cache per tutti gli endpoint. È possibile creare criteri di base per tutti gli endpoint, come illustrato nel codice seguente:
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));
});
Questo criterio di base consente di usare il tag-all tag per rimuovere tutti gli elementi nella cache.
Disabilitare il blocco delle risorse
Per impostazione predefinita, il blocco delle risorse è abilitato per attenuare il rischio di cache stampede e fulmine. Per altre informazioni, vedere Memorizzazione nella cache dell'output.
Per disabilitare il blocco delle risorse, chiamare il metodo SetLocking(false) durante la creazione di un criterio, come illustrato nell'esempio seguente:
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));
});
L'esempio seguente seleziona i criteri senza blocco per un endpoint in un'app per le API minimal:
app.MapGet("/nolock", Gravatar.WriteGravatar)
.CacheOutput("NoLock");
In un'API basata su controller usare l'attributo per selezionare i criteri:
[ApiController]
[Route("/[controller]")]
[OutputCache(PolicyName = "NoLock")]
public class NoLockController : ControllerBase
{
public async Task GetAsync()
{
await Gravatar.WriteGravatar(HttpContext);
}
}
Configurare i limiti
Le proprietà seguenti della OutputCacheOptions classe consentono di configurare i limiti applicabili a tutti gli endpoint:
- La SizeLimit proprietà imposta le dimensioni massime per l'archiviazione della cache. Quando viene raggiunto il limite, non vengono memorizzate nuove risposte nella cache fino a quando non vengono rimosse le voci meno recenti. Il valore predefinito è 100 MB.
- La MaximumBodySize proprietà imposta le dimensioni massime per il corpo della risposta. Se il corpo della risposta supera il limite, non viene memorizzato nella cache. Il valore predefinito è 64 MB.
- La DefaultExpirationTimeSpan proprietà imposta la durata massima memorizzata nella cache di una risposta, quando un'ora non viene specificata in un criterio. Il valore predefinito è 60 secondi.
Esplorare le opzioni di archiviazione della cache
L'interfaccia IOutputCacheStore è utilizzata per l'archiviazione. Per impostazione predefinita, viene usata con la MemoryCache classe . Le risposte memorizzate nella cache vengono archiviate in-process, quindi ogni server dispone di una cache separata che viene persa ogni volta che il processo del server viene riavviato.
Alternativa: Cache Redis
Un'alternativa consiste nell'usare la cache Redis . La cache Redis garantisce coerenza tra i nodi del server tramite una cache condivisa che sopravvive ai singoli processi del server. Per usare Redis per la memorizzazione nella cache di output:
Installare il pacchetto NuGet Microsoft.AspNetCore.OutputCaching.StackExchangeRedis .
Chiamare il metodo
builder.Services.AddStackExchangeRedisOutputCache(non il metodoAddStackExchangeRedisCache) e fornire un stringa di connessione che punta a un server Redis.Per esempio:
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))); });La proprietà options.Configuration è una stringa di connessione a un server Redis locale o a un'offerta ospitata, ad esempio cache di Azure per Redis. Ad esempio,
<instance_name>.redis.cache.windows.net:6380,password=,pw,ssl=True,abortConnect=Falseper cache di Azure per Redis.(Facoltativo) Opzioni . La proprietà InstanceName specifica una partizione logica per la cache.
Le opzioni di configurazione sono identiche alle opzioni di memorizzazione nella cache distribuita basate su Redis.
Non consigliato: IDistributedCache
L'interfaccia IDistributedCache non è consigliata per l'uso con il caching dell'output. Questa interfaccia non fornisce funzionalità atomiche, necessarie per l'assegnazione di tag.
L'approccio consigliato consiste nell'usare il supporto predefinito per Redis o creare un'implementazione personalizzata IOutputCacheStore usando dipendenze dirette dal meccanismo di archiviazione sottostante.
Contenuti correlati
Questo articolo illustra come configurare il middleware di caching di output in un'app ASP.NET Core. Per un'introduzione alla memorizzazione nella cache dell'output, vedere Memorizzazione nella cache dell'output.
Il middleware di caching dell'output può essere usato in tutti i tipi di app ASP.NET Core: API minimali, API Web con controller, MVC e Razor Pagine. L'app di esempio è un'API minima, ma ogni funzionalità di memorizzazione nella cache illustrata è supportata anche negli altri tipi di app.
Aggiungere il middleware all'app
Aggiungere il middleware di cache dell'output alla raccolta di servizi chiamando AddOutputCache.
Aggiungere il middleware alla pipeline di elaborazione delle richieste chiamando UseOutputCache.
Note
- Nelle app che usano middleware CORS,
UseOutputCachedeve essere chiamato dopo UseCors. - Nelle app Pages e nelle app con controller, Razor deve essere chiamato dopo
UseOutputCache. - Nelle app che usano l'autenticazione o l'autorizzazione,
UseOutputCacheè necessario chiamare dopo UseAuthentication e UseAuthorization. In caso contrario, il middleware per la memorizzazione nella cache dell'output può servire contenuto memorizzato nella cache per utenti non autorizzati invece del contenuto per gli utenti autorizzati. - La chiamata
AddOutputCacheeUseOutputCachenon avvia la memorizzazione nella cache, ma la rende disponibile. La memorizzazione nella cache dei dati di risposta deve essere configurata come illustrato nelle sezioni seguenti.
Configurare un endpoint o una pagina
Per le app per le API minime, configurare un endpoint per eseguire la memorizzazione nella cache chiamando CacheOutputo applicando l'attributo [OutputCache] , come illustrato negli esempi seguenti:
app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
app.MapGet("/attribute", [OutputCache] (context) =>
Gravatar.WriteGravatar(context));
Per le app con controller, applicare l'attributo [OutputCache] al metodo di azione. Per Razor le app Pages, applicare l'attributo alla Razor classe della pagina.
Configurare più endpoint o pagine
Creare politiche durante la chiamata a AddOutputCache per specificare la configurazione della cache applicabile a più endpoint. È possibile selezionare un criterio per endpoint specifici, mentre un criterio di base fornisce la configurazione di memorizzazione nella cache predefinita per una raccolta di endpoint.
Il codice evidenziato seguente configura la memorizzazione nella cache per tutti gli endpoint dell'app, con scadenza di 10 secondi. Se non viene specificata un'ora di scadenza, l'impostazione predefinita è un minuto.
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)));
});
Il codice evidenziato seguente crea due criteri, ognuno che specifica un'ora di scadenza diversa. Gli endpoint selezionati possono usare la scadenza di 20 secondi e altri possono usare la scadenza di 30 secondi.
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)));
});
È possibile selezionare un criterio per un endpoint quando si chiama il CacheOutput metodo o si usa l'attributo [OutputCache] :
app.MapGet("/20", Gravatar.WriteGravatar).CacheOutput("Expire20");
app.MapGet("/30", [OutputCache(PolicyName = "Expire30")] (context) =>
Gravatar.WriteGravatar(context));
Per le app con controller, applicare l'attributo [OutputCache] al metodo di azione. Per Razor le app Pages, applicare l'attributo alla Razor classe della pagina.
Politica di memorizzazione nella cache dell'output predefinita
Per impostazione predefinita, la memorizzazione nella cache dell'output segue queste regole:
- Vengono memorizzate nella cache solo le risposte HTTP 200.
- Vengono memorizzate nella cache solo le richieste HTTP GET o HEAD.
- Le risposte che impostano i cookie non vengono memorizzate nella cache.
- Le risposte alle richieste autenticate non vengono memorizzate nella cache.
Il codice seguente applica tutte le regole di memorizzazione nella cache predefinite a tutti gli endpoint di un'app:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Cache());
});
Sovrascrivere i criteri predefiniti
Il codice seguente illustra come eseguire l'override delle regole predefinite. Le righe evidenziate nel codice dei criteri personalizzati seguente abilitano la memorizzazione nella cache per i metodi HTTP POST e le risposte 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;
}
}
Per usare questo criterio personalizzato, creare un criterio denominato:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", MyCustomPolicy.Instance);
});
Selezionare il criterio specificato per un endpoint:
app.MapPost("/cachedpost", Gravatar.WriteGravatar)
.CacheOutput("CachePost");
Sostituzione alternativa delle politiche predefinite
In alternativa, usa Dependency Injection (DI) per inizializzare un'istanza, con le seguenti modifiche alla classe di criteri personalizzati:
- Un costruttore pubblico anziché un costruttore privato.
- Eliminare la
Instanceproprietà nella classe di criteri personalizzata.
Per esempio:
public sealed class MyCustomPolicy2 : IOutputCachePolicy
{
public MyCustomPolicy2()
{
}
Il resto della classe è uguale a quello illustrato in precedenza. Aggiungere i criteri personalizzati come illustrato nell'esempio seguente:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", builder =>
builder.AddPolicy<MyCustomPolicy2>(), true);
});
Il codice precedente usa DI per creare l'istanza della classe di criteri personalizzata. Tutti gli argomenti pubblici nel costruttore vengono risolti.
Quando si usa un criterio personalizzato come criterio di base, non chiamare OutputCache() (senza argomenti) in alcun endpoint a cui applicare i criteri di base. La chiamata OutputCache() aggiunge i criteri predefiniti all'endpoint.
Specificare la chiave della cache
Per impostazione predefinita, ogni parte dell'URL viene inclusa come chiave di una voce della cache, ovvero lo schema, l'host, la porta, il percorso e la stringa di query. Tuttavia, potrebbe essere necessario controllare in modo esplicito la chiave della cache. Si supponga, ad esempio, di avere un endpoint che restituisce una risposta univoca solo per ogni valore univoco della culture stringa di query. La variazione in altre parti dell'URL, ad esempio altre stringhe di query, non dovrebbe comportare voci di cache diverse. È possibile specificare tali regole in un criterio, come illustrato nel codice evidenziato seguente:
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));
});
È quindi possibile selezionare i VaryByQuery criteri per un endpoint:
app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput("Query");
Ecco alcune delle opzioni per controllare la chiave della cache:
SetVaryByQuery - Specificare uno o più nomi di stringa di query da aggiungere alla chiave della cache.
SetVaryByHeader - Specificare una o più intestazioni HTTP da aggiungere alla chiave della cache.
VaryByValue- Specificare un valore da aggiungere alla chiave della cache. Nell'esempio seguente viene utilizzato un valore che indica se il tempo del server corrente in secondi è dispari o pari. Viene generata una nuova risposta solo quando il numero di secondi passa da dispari a pari o pari a dispari.
app.MapGet("/varybyvalue", Gravatar.WriteGravatar) .CacheOutput(c => c.VaryByValue((context) => new KeyValuePair<string, string>( "time", (DateTime.Now.Second % 2) .ToString(CultureInfo.InvariantCulture))));
Usare OutputCacheOptions.UseCaseSensitivePaths per specificare che la parte del percorso della chiave fa distinzione tra maiuscole e minuscole. Il valore predefinito non fa distinzione tra maiuscole e minuscole.
Per altre opzioni, vedere la OutputCachePolicyBuilder classe .
Riconvalida della cache
La riconvalida della cache indica che il server può restituire un 304 Not Modified codice di stato HTTP anziché il corpo completo della risposta. Questo codice di stato informa il client che la risposta alla richiesta è invariata rispetto a quella ricevuta in precedenza dal client.
Il codice seguente illustra l'uso di un'intestazione Etag per abilitare la riconvalida della cache. Se il client invia un'intestazione If-None-Match con il valore etag di una risposta precedente e la voce della cache è aggiornata, il server restituisce 304 Non modificato anziché la risposta completa:
app.MapGet("/etag", async (context) =>
{
var etag = $"\"{Guid.NewGuid():n}\"";
context.Response.Headers.ETag = etag;
await Gravatar.WriteGravatar(context);
}).CacheOutput();
Un altro modo per eseguire la riconvalida della cache consiste nel controllare la data di creazione dell'elemento della cache rispetto alla data richiesta dal cliente. Quando viene specificata l'intestazione If-Modified-Since della richiesta, la memorizzazione nella cache dell'output restituisce 304 se la voce memorizzata nella cache è precedente e non è scaduta.
La riconvalida della cache è automatica in risposta a queste intestazioni inviate dal client. Non è necessaria alcuna configurazione speciale nel server per abilitare questo comportamento, oltre ad abilitare la memorizzazione nella cache dell'output.
Usare i tag per eliminare le voci della cache
È possibile usare i tag per identificare un gruppo di endpoint ed eliminare tutte le voci della cache del gruppo. Ad esempio, il codice seguente crea una coppia di endpoint i cui URL iniziano con "blog" e li contrassegna "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"));
Un modo alternativo per assegnare tag per la stessa coppia di endpoint consiste nel definire un criterio di base applicabile agli endpoint che iniziano con 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));
});
Un'altra alternativa consiste nel chiamare MapGroup:
var blog = app.MapGroup("blog")
.CacheOutput(builder => builder.Tag("tag-blog"));
blog.MapGet("/", Gravatar.WriteGravatar);
blog.MapGet("/post/{id}", Gravatar.WriteGravatar);
Negli esempi di assegnazione di tag precedenti, entrambi gli endpoint vengono identificati dal tag-blog tag . È quindi possibile eliminare le voci della cache per tali endpoint con una singola istruzione che fa riferimento a tale tag.
app.MapPost("/purge/{tag}", async (IOutputCacheStore cache, string tag) =>
{
await cache.EvictByTagAsync(tag, default);
});
Con questo codice, una richiesta HTTP POST inviata a https://localhost:<port>/purge/tag-blog rimuoverà le voci della cache per questi endpoint.
Potresti voler rimuovere tutte le voci della cache per tutti gli endpoint. A tale scopo, creare un criterio di base per tutti gli endpoint come il codice seguente:
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));
});
Questo criterio di base consente di usare il tag "tag-all" per rimuovere tutti gli elementi nella cache.
Disabilitare il blocco delle risorse
Per impostazione predefinita, il blocco delle risorse è abilitato per attenuare il rischio di cache stampede e fulmine. Per altre informazioni, vedere Memorizzazione nella cache dell'output.
Per disabilitare il blocco delle risorse, chiamare SetLocking(false) durante la creazione di un criterio, come illustrato nell'esempio seguente:
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));
});
Nell'esempio seguente sono selezionati i criteri di no-locking per un endpoint:
app.MapGet("/nolock", Gravatar.WriteGravatar)
.CacheOutput("NoLock");
Limits
Le seguenti proprietà di OutputCacheOptions consentono di configurare i limiti applicabili a tutti gli endpoint:
- SizeLimit - Dimensioni massime dell'archiviazione della cache. Quando viene raggiunto questo limite, non verranno memorizzate nuove risposte nella cache fino a quando non vengono rimosse le voci meno recenti. Il valore predefinito è 100 MB.
- MaximumBodySize - Se il corpo della risposta supera questo limite, non verrà memorizzato nella cache. Il valore predefinito è 64 MB.
- DefaultExpirationTimeSpan - La durata di scadenza che si applica quando non specificato da una policy. Il valore predefinito è 60 secondi.
Archiviazione cache
IOutputCacheStore viene usato per l'archiviazione. Per impostazione predefinita, viene usata con MemoryCache. Non consigliamo di usare IDistributedCache con la memorizzazione nella cache dell'output.
IDistributedCache non dispone di funzionalità atomiche, necessarie per l'assegnazione di tag. È consigliabile creare implementazioni personalizzate IOutputCacheStore usando dipendenze dirette sul meccanismo di archiviazione sottostante, ad esempio Redis. In alternativa, usare il supporto predefinito per la cache Redis in .NET 8..