Middleware de cache de resposta no ASP.NET Core

Por John Luo e Rick Anderson

Este artigo explica como configurar o Middleware de Cache de Resposta em um aplicativo ASP.NET Core. O middleware determina quando as respostas podem ser armazenadas em cache, armazena respostas e atende respostas do cache. Para obter uma introdução ao cache HTTP e ao [ResponseCache] atributo, consulte Cache de Resposta.

O middleware de cache de resposta:

  • Habilita o cache de respostas do servidor com base em cabeçalhos de cache HTTP. Implementa a semântica de cache HTTP padrão. Caches com base em cabeçalhos de cache HTTP, como proxies fazem.
  • Normalmente, não é benéfico para aplicativos de interface do usuário, como Razor o Pages, porque os navegadores geralmente definem cabeçalhos de solicitação que impedem o cache. O cache de saída está sendo considerado para a próxima versão do ASP.NET Core, o que beneficiará os aplicativos de interface do usuário. Com o cache de saída, a configuração decide o que deve ser armazenado em cache independentemente dos cabeçalhos HTTP. Saiba mais neste tópico do GitHub.
  • Pode ser benéfico para solicitações de API GET ou HEAD públicas de clientes em que as condições de cache são atendidas .

Use Fiddler, Postman ou outra ferramenta que possa definir explicitamente cabeçalhos de solicitação. Definir cabeçalhos explicitamente é preferencial para testar o cache. Para obter mais informações, consulte Solução de problemas

Configuração

In Program.cs, add the Response Caching Middleware services AddResponseCaching to the service collection and configure the app to use the middleware with the UseResponseCaching extension method. UseResponseCaching adiciona o middleware ao pipeline de processamento de solicitação:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCaching();

var app = builder.Build();

app.UseHttpsRedirection();

// UseCors must be called before UseResponseCaching
//app.UseCors();

app.UseResponseCaching();

Aviso

UseCors deve ser chamado antes UseResponseCaching ao usar middleware CORS.

O aplicativo de exemplo adiciona cabeçalhos para controlar o cache em solicitações subsequentes:

  • Cache-Control: armazena em cache respostas em cache por até 10 segundos.
  • Varie: configura o middleware para atender a uma resposta armazenada em cache somente se o cabeçalho Aceitar codificação de solicitações subsequentes corresponder ao da solicitação original.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCaching();

var app = builder.Build();

app.UseHttpsRedirection();

// UseCors must be called before UseResponseCaching
//app.UseCors();

app.UseResponseCaching();

app.Use(async (context, next) =>
{
    context.Response.GetTypedHeaders().CacheControl =
        new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
        {
            Public = true,
            MaxAge = TimeSpan.FromSeconds(10)
        };
    context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
        new string[] { "Accept-Encoding" };

    await next();
});

app.MapGet("/", () => DateTime.Now.Millisecond);

app.Run();

Os cabeçalhos anteriores não são gravados na resposta e são substituídos quando um controlador, uma ação ou Razor uma página:

  • Tem um atributo [ResponseCache] . Isso se aplica mesmo se uma propriedade não estiver definida. Por exemplo, omitir a propriedade VaryByHeader fará com que o cabeçalho correspondente seja removido da resposta.

O Middleware de Cache de Resposta armazena em cache apenas as respostas do servidor que resultam em um código de status 200 (OK). Quaisquer outras respostas, incluindo páginas de erro, são ignoradas pelo middleware.

Aviso

As respostas que contêm conteúdo para clientes autenticados devem ser marcadas como não em cache para impedir que o middleware armazene e forneça essas respostas. Consulte Condições de cache para obter detalhes sobre como o middleware determina se uma resposta pode ser armazenada em cache.

O código anterior normalmente não retorna um valor armazenado em cache para um navegador. Use Fiddler, Postman ou outra ferramenta que pode definir explicitamente cabeçalhos de solicitação e é preferencial para testar o cache. Para obter mais informações, consulte Solução de problemas neste artigo.

Opções

As opções de cache de resposta são mostradas na tabela a seguir.

Opção Descrição
MaximumBodySize O maior tamanho em cache para o corpo da resposta em bytes. O valor padrão é 64 * 1024 * 1024 (64 MB).
SizeLimit O limite de tamanho para o middleware de cache de resposta em bytes. O valor padrão é 100 * 1024 * 1024 (100 MB).
UseCaseSensitivePaths Determina se as respostas são armazenadas em cache em caminhos que diferenciam maiúsculas de minúsculas. O valor padrão é false.

O exemplo a seguir configura o middleware para:

  • Respostas de cache com um tamanho de corpo menor ou igual a 1.024 bytes.
  • Armazene as respostas por caminhos que diferenciam maiúsculas de minúsculas. Por exemplo, /page1 e /Page1 são armazenados separadamente.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCaching(options =>
{
    options.MaximumBodySize = 1024;
    options.UseCaseSensitivePaths = true;
});

var app = builder.Build();

app.UseHttpsRedirection();

// UseCors must be called before UseResponseCaching
//app.UseCors();

app.UseResponseCaching();

app.Use(async (context, next) =>
{
    context.Response.GetTypedHeaders().CacheControl =
        new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
        {
            Public = true,
            MaxAge = TimeSpan.FromSeconds(10)
        };
    context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
        new string[] { "Accept-Encoding" };

    await next(context);
});

app.MapGet("/", () => DateTime.Now.Millisecond);

app.Run();

VaryByQueryKeys

Ao usar MVC, controladores de API Web ou Razor modelos de página de Páginas, o [ResponseCache] atributo especifica os parâmetros necessários para definir os cabeçalhos apropriados para o cache de resposta. O único parâmetro do [ResponseCache] atributo que exige estritamente o middleware é VaryByQueryKeys, que não corresponde a um cabeçalho HTTP real. Para obter mais informações, consulte Cache de resposta em ASP.NET Core.

Ao não usar o atributo, o [ResponseCache] cache de resposta pode ser variado com VaryByQueryKeys. Use diretamente ResponseCachingFeature do HttpContext.Features:

var responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>();

if (responseCachingFeature != null)
{
    responseCachingFeature.VaryByQueryKeys = new[] { "MyKey" };
}

Usar um único valor igual a * em VaryByQueryKeys varia o cache por todos os parâmetros de consulta de solicitação.

Cabeçalhos HTTP usados pelo Middleware de Cache de Resposta

A tabela a seguir fornece informações sobre cabeçalhos HTTP que afetam o cache de resposta.

Cabeçalho Detalhes
Authorization A resposta não será armazenada em cache se o cabeçalho existir.
Cache-Control O middleware considera apenas as respostas de cache marcadas com a public diretiva de cache. Controlar o cache com os seguintes parâmetros:
  • max-age
  • max-stale†
  • min-fresh
  • must-revalidate
  • no-cache
  • no-store
  • only-if-cached
  • particulares
  • públicos
  • s-maxage
  • proxy-revalidate‡
†Se nenhum limite for especificado max-stale, o middleware não tomará nenhuma ação.
proxy-revalidate‡ tem o mesmo efeito que must-revalidate.

Para obter mais informações, consulte RFC 7231: Solicitar diretivas Cache-Control.
Pragma Um Pragma: no-cache cabeçalho na solicitação produz o mesmo efeito que Cache-Control: no-cache. Esse cabeçalho é substituído pelas diretivas relevantes no Cache-Control cabeçalho, se presente. Considerado para compatibilidade com versões anteriores com HTTP/1.0.
Set-Cookie A resposta não será armazenada em cache se o cabeçalho existir. Qualquer middleware no pipeline de processamento de solicitação que define um ou mais cookies impede que o Middleware de Cache de Resposta armazena em cache a resposta (por exemplo, o cookieprovedor TempData baseado).
Vary O Vary cabeçalho é usado para variar a resposta armazenada em cache por outro cabeçalho. Por exemplo, armazene respostas em cache codificando incluindo o Vary: Accept-Encoding cabeçalho, que armazena em cache respostas para solicitações com cabeçalhos Accept-Encoding: gzip e Accept-Encoding: text/plain separadamente. Uma resposta com um valor de cabeçalho nunca * é armazenada.
Expires Uma resposta considerada obsoleta por esse cabeçalho não é armazenada ou recuperada, a menos que seja substituída por outros Cache-Control cabeçalhos.
If-None-Match A resposta completa será fornecida do cache se o valor não * for e a ETag resposta não corresponder a nenhum dos valores fornecidos. Caso contrário, uma resposta 304 (Não Modificada) será atendida.
If-Modified-Since Se o If-None-Match cabeçalho não estiver presente, uma resposta completa será servida do cache se a data da resposta armazenada em cache for mais recente do que o valor fornecido. Caso contrário, uma resposta 304 – Não Modificada será atendida.
Date Ao servir do cache, o Date cabeçalho será definido pelo middleware se não tiver sido fornecido na resposta original.
Content-Length Ao servir do cache, o Content-Length cabeçalho será definido pelo middleware se não tiver sido fornecido na resposta original.
Age O Age cabeçalho enviado na resposta original é ignorado. O middleware calcula um novo valor ao servir uma resposta armazenada em cache.

O cache respeita as diretivas de Cache-Control de solicitação

O middleware respeita as regras da especificação de cache HTTP 1.1. As regras exigem um cache para honrar um cabeçalho válido Cache-Control enviado pelo cliente. De acordo com a especificação, um cliente pode fazer solicitações com um no-cache valor de cabeçalho e forçar o servidor a gerar uma nova resposta para cada solicitação. Atualmente, não há controle do desenvolvedor sobre esse comportamento de cache ao usar o middleware porque o middleware adere à especificação oficial de cache.

Para obter mais controle sobre o comportamento de cache, explore outros recursos de cache do ASP.NET Core. Consulte os seguintes tópicos:

Solução de problemas

O Middleware de Cache de Resposta é IMemoryCacheusado, que tem uma capacidade limitada. Quando a capacidade é excedida, o cache de memória é compactado.

Se o comportamento de cache não for o esperado, confirme se as respostas podem ser armazenadas em cache e capazes de serem atendidas no cache. Examine os cabeçalhos de entrada da solicitação e os cabeçalhos de saída da resposta. Habilite o registro em log para ajudar na depuração.

Ao testar e solucionar problemas de comportamento de cache, um navegador normalmente define cabeçalhos de solicitação que impedem o cache. Por exemplo, um navegador pode definir o Cache-Control cabeçalho como no-cache ou max-age=0 ao atualizar uma página. Fiddler, Postman e outras ferramentas podem definir explicitamente cabeçalhos de solicitação e são preferenciais para testar o cache.

Condições para cache

  • A solicitação deve resultar em uma resposta de servidor com um código de status 200 (OK).
  • O método de solicitação deve ser GET ou HEAD.
  • Middleware de cache de resposta deve ser colocado antes do middleware que exige cache. Para obter mais informações, consulte ASP.NET Core Middleware.
  • O Authorization cabeçalho não deve estar presente.
  • Cache-Control os parâmetros de cabeçalho devem ser válidos e a resposta deve ser marcada public e não marcada private.
  • O Pragma: no-cache cabeçalho não deverá estar presente se o Cache-Control cabeçalho não estiver presente, pois o Cache-Control cabeçalho substituirá o Pragma cabeçalho quando estiver presente.
  • O Set-Cookie cabeçalho não deve estar presente.
  • Vary os parâmetros de cabeçalho devem ser válidos e não iguais a *.
  • O valor do Content-Length cabeçalho (se definido) deve corresponder ao tamanho do corpo da resposta.
  • O IHttpSendFileFeature não é usado.
  • A resposta não deve ser obsoleta conforme especificado pelo Expires cabeçalho e pelas max-age diretivas e s-maxage cache.
  • O buffer de resposta deve ser bem-sucedido. O tamanho da resposta deve ser menor que o configurado ou padrão SizeLimit. O tamanho do corpo da resposta deve ser menor que o configurado ou padrão MaximumBodySize.
  • A resposta deve ser armazenada em cache de acordo com as especificações do RFC 7234 . Por exemplo, a no-store diretiva não deve existir nos campos de cabeçalho de solicitação ou resposta. Consulte a Seção 3: Armazenando respostas em caches do RFC 7234 para obter detalhes.

Observação

O sistema Antiforgery para gerar tokens seguros para evitar ataques CSRF (solicitação de site cruzado) define os cabeçalhos e Pragma os cabeçalhos para no-cache que as Cache-Control respostas não sejam armazenadas em cache. Para obter informações sobre como desabilitar tokens antiforgeria para elementos de formulário HTML, consulte Evitar ataques XSRF/CSRF (Solicitação de Sites Cruzados) em ASP.NET Core.

Recursos adicionais

Este artigo explica como configurar o Middleware de Cache de Resposta em um aplicativo ASP.NET Core. O middleware determina quando as respostas podem ser armazenadas em cache, armazena respostas e atende respostas do cache. Para obter uma introdução ao cache HTTP e ao [ResponseCache] atributo, consulte Cache de Resposta.

Exibir ou baixar código de exemplo (como baixar)

Configuração

O Middleware de Cache de Resposta está implicitamente disponível para ASP.NET Core aplicativos por meio da estrutura compartilhada.

In Startup.ConfigureServices, adicione o Middleware de Cache de Resposta à coleção de serviços:

public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCaching();
    services.AddRazorPages();
}

Configure o aplicativo para usar o middleware com o UseResponseCaching método de extensão, que adiciona o middleware ao pipeline de processamento de solicitação em Startup.Configure:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
    }

    app.UseStaticFiles();
    app.UseRouting();
    // UseCors must be called before UseResponseCaching
    // app.UseCors("myAllowSpecificOrigins");

    app.UseResponseCaching();

    app.Use(async (context, next) =>
    {
        context.Response.GetTypedHeaders().CacheControl = 
            new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
            {
                Public = true,
                MaxAge = TimeSpan.FromSeconds(10)
            };
        context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] = 
            new string[] { "Accept-Encoding" };

        await next();
    });

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Aviso

UseCors deve ser chamado antes UseResponseCaching ao usar middleware CORS.

O aplicativo de exemplo adiciona cabeçalhos para controlar o cache em solicitações subsequentes:

  • Cache-Control: armazena em cache respostas em cache por até 10 segundos.
  • Varie: configura o middleware para atender a uma resposta armazenada em cache somente se o cabeçalho Aceitar codificação de solicitações subsequentes corresponder ao da solicitação original.
// using Microsoft.AspNetCore.Http;

app.Use(async (context, next) =>
{
    context.Response.GetTypedHeaders().CacheControl = 
        new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
        {
            Public = true,
            MaxAge = TimeSpan.FromSeconds(10)
        };
    context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] = 
        new string[] { "Accept-Encoding" };

    await next();
});

Os cabeçalhos anteriores não são gravados na resposta e são substituídos quando um controlador, uma ação ou Razor uma página:

  • Tem um atributo [ResponseCache] . Isso se aplica mesmo se uma propriedade não estiver definida. Por exemplo, omitir a propriedade VaryByHeader fará com que o cabeçalho correspondente seja removido da resposta.

O Middleware de Cache de Resposta armazena em cache apenas as respostas do servidor que resultam em um código de status 200 (OK). Quaisquer outras respostas, incluindo páginas de erro, são ignoradas pelo middleware.

Aviso

As respostas que contêm conteúdo para clientes autenticados devem ser marcadas como não em cache para impedir que o middleware armazene e forneça essas respostas. Consulte Condições de cache para obter detalhes sobre como o middleware determina se uma resposta pode ser armazenada em cache.

Opções

As opções de cache de resposta são mostradas na tabela a seguir.

Opção Descrição
MaximumBodySize O maior tamanho em cache para o corpo da resposta em bytes. O valor padrão é 64 * 1024 * 1024 (64 MB).
SizeLimit O limite de tamanho para o middleware de cache de resposta em bytes. O valor padrão é 100 * 1024 * 1024 (100 MB).
UseCaseSensitivePaths Determina se as respostas são armazenadas em cache em caminhos que diferenciam maiúsculas de minúsculas. O valor padrão é false.

O exemplo a seguir configura o middleware para:

  • Respostas de cache com um tamanho de corpo menor ou igual a 1.024 bytes.
  • Armazene as respostas por caminhos que diferenciam maiúsculas de minúsculas. Por exemplo, /page1 e /Page1 são armazenados separadamente.
services.AddResponseCaching(options =>
{
    options.MaximumBodySize = 1024;
    options.UseCaseSensitivePaths = true;
});

VaryByQueryKeys

Ao usar controladores de API web/MVC ou Razor modelos de página de Páginas, o [ResponseCache] atributo especifica os parâmetros necessários para definir os cabeçalhos apropriados para o cache de resposta. O único parâmetro do [ResponseCache] atributo que exige estritamente o middleware é VaryByQueryKeys, que não corresponde a um cabeçalho HTTP real. Para obter mais informações, consulte Cache de resposta em ASP.NET Core.

Ao não usar o atributo, o [ResponseCache] cache de resposta pode ser variado com VaryByQueryKeys. Use diretamente ResponseCachingFeature do HttpContext.Features:

var responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>();

if (responseCachingFeature != null)
{
    responseCachingFeature.VaryByQueryKeys = new[] { "MyKey" };
}

Usar um único valor igual a * em VaryByQueryKeys varia o cache por todos os parâmetros de consulta de solicitação.

Cabeçalhos HTTP usados pelo Middleware de Cache de Resposta

A tabela a seguir fornece informações sobre cabeçalhos HTTP que afetam o cache de resposta.

Cabeçalho Detalhes
Authorization A resposta não será armazenada em cache se o cabeçalho existir.
Cache-Control O middleware considera apenas as respostas de cache marcadas com a public diretiva de cache. Controlar o cache com os seguintes parâmetros:
  • max-age
  • max-stale†
  • min-fresh
  • must-revalidate
  • no-cache
  • no-store
  • somente se armazenado em cache
  • particulares
  • públicos
  • s-maxage
  • proxy-revalidate‡
†Se nenhum limite for especificado max-stale, o middleware não tomará nenhuma ação.
proxy-revalidate‡ tem o mesmo efeito que must-revalidate.

Para obter mais informações, consulte RFC 7231: Solicitar diretivas de Cache-Control.
Pragma Um Pragma: no-cache cabeçalho na solicitação produz o mesmo efeito que Cache-Control: no-cache. Esse cabeçalho é substituído pelas diretivas relevantes no Cache-Control cabeçalho, se presente. Considerado para compatibilidade com versões anteriores com HTTP/1.0.
Set-Cookie A resposta não será armazenada em cache se o cabeçalho existir. Qualquer middleware no pipeline de processamento de solicitação que define um ou mais cookies impede que o Middleware de Cache de Resposta armazena em cache a resposta (por exemplo, o cookieprovedor TempData baseado).
Vary O Vary cabeçalho é usado para variar a resposta armazenada em cache por outro cabeçalho. Por exemplo, armazene respostas em cache codificando incluindo o Vary: Accept-Encoding cabeçalho, que armazena em cache respostas para solicitações com cabeçalhos Accept-Encoding: gzip e Accept-Encoding: text/plain separadamente. Uma resposta com um valor de cabeçalho nunca * é armazenada.
Expires Uma resposta considerada obsoleta por esse cabeçalho não é armazenada ou recuperada, a menos que seja substituída por outros Cache-Control cabeçalhos.
If-None-Match A resposta completa será fornecida do cache se o valor não * for e a ETag resposta não corresponder a nenhum dos valores fornecidos. Caso contrário, uma resposta 304 (Não Modificada) será atendida.
If-Modified-Since Se o If-None-Match cabeçalho não estiver presente, uma resposta completa será atendida do cache se a data de resposta armazenada em cache for mais recente do que o valor fornecido. Caso contrário, uma resposta 304 – Não modificada será atendida.
Date Ao servir do cache, o Date cabeçalho será definido pelo middleware se ele não tiver sido fornecido na resposta original.
Content-Length Ao servir do cache, o Content-Length cabeçalho será definido pelo middleware se ele não tiver sido fornecido na resposta original.
Age O Age cabeçalho enviado na resposta original é ignorado. O middleware calcula um novo valor ao servir uma resposta armazenada em cache.

O cache respeita as diretivas de Cache-Control de solicitação

O middleware respeita as regras da especificação de cache HTTP 1.1. As regras exigem um cache para honrar um cabeçalho válido Cache-Control enviado pelo cliente. De acordo com a especificação, um cliente pode fazer solicitações com um no-cache valor de cabeçalho e forçar o servidor a gerar uma nova resposta para cada solicitação. Atualmente, não há controle do desenvolvedor sobre esse comportamento de cache ao usar o middleware porque o middleware segue a especificação de cache oficial.

Para obter mais controle sobre o comportamento de cache, explore outros recursos de cache de ASP.NET Core. Consulte os seguintes tópicos:

Solução de problemas

Se o comportamento de cache não for o esperado, confirme se as respostas podem ser armazenadas em cache e capazes de serem atendidas no cache. Examine os cabeçalhos de entrada da solicitação e os cabeçalhos de saída da resposta. Habilite o registro em log para ajudar na depuração.

Ao testar e solucionar problemas de comportamento de cache, um navegador pode definir cabeçalhos de solicitação que afetam o cache de maneiras indesejáveis. Por exemplo, um navegador pode definir o Cache-Control cabeçalho como no-cache ou max-age=0 ao atualizar uma página. As ferramentas a seguir podem definir explicitamente os cabeçalhos de solicitação e são preferenciais para testar o cache:

Condições de cache

  • A solicitação deve resultar em uma resposta de servidor com um código de status 200 (OK).
  • O método de solicitação deve ser GET ou HEAD.
  • Em Startup.Configure, o middleware de cache de resposta deve ser colocado antes do middleware que exija cache. Para obter mais informações, consulte ASP.NET Core Middleware.
  • O Authorization cabeçalho não deve estar presente.
  • Cache-Control os parâmetros de cabeçalho devem ser válidos e a resposta deve ser marcada public e não marcada private.
  • O Pragma: no-cache cabeçalho não deverá estar presente se o Cache-Control cabeçalho não estiver presente, pois o Cache-Control cabeçalho substituirá o Pragma cabeçalho quando presente.
  • O Set-Cookie cabeçalho não deve estar presente.
  • Vary os parâmetros de cabeçalho devem ser válidos e não iguais a *.
  • O valor do Content-Length cabeçalho (se definido) deve corresponder ao tamanho do corpo da resposta.
  • O IHttpSendFileFeature não é usado.
  • A resposta não deve ser obsoleta conforme especificado pelo Expires cabeçalho e pelas max-age diretivas e s-maxage cache.
  • O buffer de resposta deve ser bem-sucedido. O tamanho da resposta deve ser menor que o configurado ou padrão SizeLimit. O tamanho do corpo da resposta deve ser menor do que o configurado ou padrão MaximumBodySize.
  • A resposta deve ser armazenada em cache de acordo com as especificações do RFC 7234 . Por exemplo, a no-store diretiva não deve existir em campos de cabeçalho de solicitação ou resposta. Consulte a Seção 3: Armazenando respostas em caches do RFC 7234 para obter detalhes.

Observação

O sistema Antiforgery para gerar tokens seguros para evitar ataques CSRF (Solicitação entre Sites) define os cabeçalhos e Pragma os Cache-Control cabeçalhos para no-cache que as respostas não sejam armazenadas em cache. Para obter informações sobre como desabilitar tokens antiforgeria para elementos de formulário HTML, consulte Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core.

Recursos adicionais