ПО промежуточного слоя для кэширования ответов в ASP.NET Core

Джон Луо и Рик Андерсон

В этой статье объясняется, как настроить ПО промежуточного слоя кэширования ответа в приложении ASP.NET Core. ПО промежуточного слоя определяет, когда ответы кэшируются, хранятся ответы и служат ответами из кэша. Общие сведения о кэшировании HTTP и [ResponseCache] атрибуте см. в разделе "Кэширование ответов".

По промежуточному слоям кэширования ответа:

  • Включает кэширование ответов сервера на основе заголовков кэша HTTP. Реализует стандартную семантику кэширования HTTP. Кэши на основе заголовков кэша HTTP, таких как прокси-серверы.
  • Обычно не подходит для приложений пользовательского интерфейса, таких как Pages, так как Razor браузеры обычно задают заголовки запросов, которые предотвращают кэширование. Кэширование выходных данных, доступное в ASP.NET Core 7.0 и более поздних версий, обеспечивает преимущества приложений пользовательского интерфейса. При кэшировании выходных данных конфигурация решает, что следует кэшировать независимо от заголовков HTTP.
  • Может оказаться полезным для общедоступных запросов GET или HEAD API от клиентов, где выполняются условия кэширования .

Чтобы проверить кэширование ответов, используйте Fiddler или другое средство, которое может явно задать заголовки запросов. Для тестирования кэширования предпочтительнее явно задать заголовки. Дополнительные сведения см. в разделе Устранение неполадок.

Настройка

Добавьте Program.csслужбы AddResponseCaching промежуточного слоя для кэширования ответа в коллекцию служб и настройте приложение для использования по промежуточного слоя с методом UseResponseCaching расширения. UseResponseCaching добавляет ПО промежуточного слоя в конвейер обработки запросов:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCaching();

var app = builder.Build();

app.UseHttpsRedirection();

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

app.UseResponseCaching();

Предупреждение

UseCors необходимо вызывать до UseResponseCaching использования ПО промежуточного слоя CORS.

Пример приложения добавляет заголовки для управления кэшированием при последующих запросах:

  • Элемент управления кэшем: кэширует кэшируемые ответы до 10 секунд.
  • Зависит. Настраивает ПО промежуточного слоя для обслуживания кэшированного ответа только в том случае, если заголовок Accept-Encoding последующих запросов соответствует исходному запросу.
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();

Предыдущие заголовки не записываются в ответ и переопределяются при переопределении контроллера, действия или Razor страницы:

По промежуточному слоям для кэширования ответа кэширует только ответы сервера, которые приводят к коду состояния 200 (ОК). Любые другие ответы, включая страницы ошибок, игнорируются ПО промежуточного слоя.

Предупреждение

Ответы, содержащие содержимое для прошедших проверку подлинности клиентов, должны быть помечены как не кэшируемые, чтобы предотвратить хранение и обслуживание этих ответов по промежуточному по промежуточному слоям. Дополнительные сведения о том, как ПО промежуточного слоя определяет, является ли кэшируемый ответ, см . в условиях кэширования .

Предыдущий код обычно не возвращает кэшированное значение в браузер. Используйте Fiddler, Postman или другое средство, которое может явно задать заголовки запросов и предпочтительнее для тестирования кэширования. Дополнительные сведения см . в статье об устранении неполадок.

Параметры

Параметры кэширования ответов показаны в следующей таблице.

Вариант Описание
MaximumBodySize Самый большой кэшируемый размер текста ответа в байтах. Значение по умолчанию — 64 * 1024 * 1024 64 МБ.
SizeLimit Ограничение размера ПО промежуточного слоя кэша ответа в байтах. Значение по умолчанию — 100 * 1024 * 1024 100 МБ.
UseCaseSensitivePaths Определяет, кэшируются ли ответы на пути с учетом регистра. Значение по умолчанию — false.

Следующий пример настраивает ПО промежуточного слоя следующим образом:

  • Ответы кэша с размером тела меньше или равным 1024 байтам.
  • Сохраните ответы по путям с учетом регистра. Например, /page1 и /Page1 хранятся отдельно.
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

При использовании MVC, контроллеров веб-API или Razor моделей страниц Страниц атрибут задает параметры, [ResponseCache] необходимые для задания соответствующих заголовков для кэширования ответов. Единственным параметром [ResponseCache] атрибута, который строго требует по промежуточного слоя, является VaryByQueryKeysто, что не соответствует фактическому заголовку HTTP. Дополнительные сведения см. в статье Кэширование ответов в ASP.NET Core.

Если атрибут не используется, кэширование ответов [ResponseCache] может быть разнообразным.VaryByQueryKeys ResponseCachingFeature Используйте непосредственно из httpContext.Features:

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

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

Использование одного значения, равного * значению в VaryByQueryKeys кэше, зависит от всех параметров запроса запроса.

Заголовки HTTP, используемые по промежуточному слоям кэширования ответа

В следующей таблице содержатся сведения о заголовках HTTP, влияющих на кэширование ответов.

Верхний колонтитул Сведения
Authorization Ответ не кэширован, если заголовок существует.
Cache-Control ПО промежуточного слоя учитывает только ответы кэширования, помеченные директивой кэша public . Управление кэшированием со следующими параметрами:
  • max-age
  • max-устаревший†
  • min-fresh
  • must-revalidate
  • no-cache
  • no-store
  • только в случае кэширования
  • private
  • public
  • S-maxage
  • proxy-revalidate" ( Прокси-revalidate)
†Если не задано max-staleограничение, по промежуточному слоям не выполняется никаких действий.
proxy-revalidate имеет тот же эффект, что must-revalidateи .

Дополнительные сведения см. в статье RFC 9111: директивы запроса.
Pragma Заголовок Pragma: no-cache в запросе создает тот же эффект, что Cache-Control: no-cacheи . Этот заголовок переопределяется соответствующими директивами в заголовке Cache-Control , если он присутствует. Рекомендуется обеспечить обратную совместимость с HTTP/1.0.
Set-Cookie Ответ не кэширован, если заголовок существует. Любое ПО промежуточного слоя в конвейере обработки запросов, задающее один или несколько cookies, запрещает кэширование по промежуточному слоям ответа (например, cookieпоставщик TempData на основе ответа).
Vary Заголовок Vary используется для изменения кэшированного ответа по другому заголовку. Например, ответы кэша кодируются путем кодирования, включая Vary: Accept-Encoding заголовок, который кэширует ответы на запросы с заголовками Accept-Encoding: gzip и Accept-Encoding: text/plain отдельно. Ответ со значением заголовка * никогда не сохраняется.
Expires Ответ, который считается устаревшим по этому заголовку, не сохраняется или извлекается, если не переопределяется другими Cache-Control заголовками.
If-None-Match Полный ответ обслуживается из кэша, если значение не * равно, а ETag ответ не соответствует ни одному из указанных значений. В противном случае обслуживается ответ 304 (не изменено).
If-Modified-Since If-None-Match Если заголовок отсутствует, полный ответ обслуживается из кэша, если дата кэшированного ответа является более новой, чем указанное значение. В противном случае отдается ответ 304 — не изменен.
Date При обслуживании из кэша заголовок устанавливается по промежуточному слоям, Date если он не был указан в исходном ответе.
Content-Length При обслуживании из кэша заголовок устанавливается по промежуточному слоям, Content-Length если он не был указан в исходном ответе.
Age Заголовок, Age отправленный в исходном ответе, игнорируется. ПО промежуточного слоя вычисляет новое значение при обслуживании кэшированного ответа.

Кэширование учитывает директивы Cache-Control для запросов

ПО промежуточного слоя учитывает правила RFC 9111: кэширование HTTP (раздел 5.2. Cache-Control). Для выполнения допустимых Cache-Control заголовков, отправленных клиентом, требуется кэш. В соответствии со спецификацией клиент может выполнять запросы со no-cache значением заголовка и принудительно создавать новый ответ для каждого запроса. В настоящее время при использовании ПО промежуточного слоя отсутствует контроль над этим поведением кэширования, так как по промежуточному слоям соответствует официальной спецификации кэширования.

Дополнительные сведения о поведении кэширования см. в других функциях кэширования ASP.NET Core. См. следующие разделы:

Устранение неполадок

По промежуточному слоям кэширования ответа используется IMemoryCache, которое имеет ограниченную емкость. При превышении емкости кэш памяти сжимается.

Если поведение кэширования не так ожидаемо, убедитесь, что ответы кэшируются и могут обслуживаться из кэша. Проверьте входящие заголовки запроса и исходящие заголовки ответа. Включите ведение журнала для отладки.

При тестировании и устранении неполадок при кэшировании браузер обычно задает заголовки запросов, которые препятствуют кэшированию. Например, браузер может задать Cache-Control заголовок no-cache или max-age=0 при обновлении страницы. Fiddler, Postman и другие средства могут явно задать заголовки запросов и предпочтительнее для тестирования кэширования.

Условия кэширования

  • Запрос должен привести к ответу сервера с кодом состояния 200 (ОК).
  • Метод запроса должен быть GET или HEAD.
  • Кэширование по промежуточному слоям ответа должно быть помещено перед по промежуточному слоям, требующим кэширования. Дополнительные сведения см. в статье ПО промежуточного слоя ASP.NET Core.
  • Заголовок Authorization не должен присутствовать.
  • Cache-Control Параметры заголовка должны быть допустимыми, а ответ должен быть помечен public и не помечен private.
  • Заголовок Pragma: no-cache не должен присутствовать, если Cache-Control заголовок отсутствует, так как Cache-Control заголовок переопределяет Pragma заголовок при наличии.
  • Заголовок Set-Cookie не должен присутствовать.
  • Vary Параметры заголовка должны быть допустимыми и не равными *.
  • Значение Content-Length заголовка (если задано) должно соответствовать размеру текста ответа.
  • Не IHttpSendFileFeature используется.
  • Ответ не должен быть устаревшим Expires , как указано в заголовке и max-ages-maxage директивах кэша.
  • Буферизация ответов должна быть успешной. Размер ответа должен быть меньше настроенного или по умолчанию SizeLimit. Размер текста ответа должен быть меньше настроенного или по умолчанию MaximumBodySize.
  • Ответ должен быть кэшируемым в соответствии с RFC 9111: кэширование HTTP. Например, директива no-store не должна существовать в полях заголовка запроса или ответа. Дополнительные сведения см. в разделе RFC 9111: кэширование HTTP (раздел 3. Хранение ответов в кэшах ).

Примечание.

Система защиты от атак для создания безопасных маркеров для предотвращения атак межсайтовых запросов forgery (CSRF) задает Cache-Control значения и Pragma заголовки таким образом, чтобы ответы no-cache не кэшировались. Сведения об отключении маркеров защиты от элементов формы HTML см. в статье "Предотвращение атак межсайтовых запросов forgery (XSRF/CSRF) в ASP.NET Core.

Дополнительные ресурсы

В этой статье объясняется, как настроить по промежуточному слоям кэширования ответов в приложении ASP.NET Core. ПО промежуточного слоя определяет, когда ответы кэшируются, хранятся ответы и служат ответами из кэша. Общие сведения о кэшировании HTTP и [ResponseCache] атрибуте см. в разделе "Кэширование ответов".

Просмотреть или скачать образец кода (описание загрузки)

Настройка

ПО промежуточного слоя кэширования ответов неявно доступно для приложений ASP.NET Core через общую платформу.

Добавьте Startup.ConfigureServicesПО промежуточного слоя кэширования ответа в коллекцию служб:

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

Настройте приложение для использования ПО промежуточного слоя с UseResponseCaching методом расширения, которое добавляет ПО промежуточного слоя в конвейер обработки запросов в 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();
    });
}

Предупреждение

UseCors необходимо вызывать до UseResponseCaching использования ПО промежуточного слоя CORS.

Пример приложения добавляет заголовки для управления кэшированием при последующих запросах:

  • Элемент управления кэшем: кэширует кэшируемые ответы до 10 секунд.
  • Зависит. Настраивает ПО промежуточного слоя для обслуживания кэшированного ответа только в том случае, если заголовок Accept-Encoding последующих запросов соответствует исходному запросу.
// 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();
});

Предыдущие заголовки не записываются в ответ и переопределяются при переопределении контроллера, действия или Razor страницы:

По промежуточному слоям для кэширования ответа кэширует только ответы сервера, которые приводят к коду состояния 200 (ОК). Любые другие ответы, включая страницы ошибок, игнорируются ПО промежуточного слоя.

Предупреждение

Ответы, содержащие содержимое для прошедших проверку подлинности клиентов, должны быть помечены как не кэшируемые, чтобы предотвратить хранение и обслуживание этих ответов по промежуточному по промежуточному слоям. Дополнительные сведения о том, как ПО промежуточного слоя определяет, является ли кэшируемый ответ, см . в условиях кэширования .

Параметры

Параметры кэширования ответов показаны в следующей таблице.

Вариант Описание
MaximumBodySize Самый большой кэшируемый размер текста ответа в байтах. Значение по умолчанию — 64 * 1024 * 1024 64 МБ.
SizeLimit Ограничение размера ПО промежуточного слоя кэша ответа в байтах. Значение по умолчанию — 100 * 1024 * 1024 100 МБ.
UseCaseSensitivePaths Определяет, кэшируются ли ответы на пути с учетом регистра. Значение по умолчанию — false.

Следующий пример настраивает ПО промежуточного слоя следующим образом:

  • Ответы кэша с размером тела меньше или равным 1024 байтам.
  • Сохраните ответы по путям с учетом регистра. Например, /page1 и /Page1 хранятся отдельно.
services.AddResponseCaching(options =>
{
    options.MaximumBodySize = 1024;
    options.UseCaseSensitivePaths = true;
});

VaryByQueryKeys

При использовании контроллеров MVC или Razor моделей страниц веб-API атрибут задает параметры, [ResponseCache] необходимые для задания соответствующих заголовков для кэширования ответов. Единственным параметром [ResponseCache] атрибута, который строго требует по промежуточного слоя, является VaryByQueryKeysто, что не соответствует фактическому заголовку HTTP. Дополнительные сведения см. в статье Кэширование ответов в ASP.NET Core.

Если атрибут не используется, кэширование ответов [ResponseCache] может быть разнообразным.VaryByQueryKeys ResponseCachingFeature Используйте непосредственно из httpContext.Features:

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

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

Использование одного значения, равного * значению в VaryByQueryKeys кэше, зависит от всех параметров запроса запроса.

Заголовки HTTP, используемые по промежуточному слоям кэширования ответа

В следующей таблице содержатся сведения о заголовках HTTP, влияющих на кэширование ответов.

Верхний колонтитул Сведения
Authorization Ответ не кэширован, если заголовок существует.
Cache-Control ПО промежуточного слоя учитывает только ответы кэширования, помеченные директивой кэша public . Управление кэшированием со следующими параметрами:
  • max-age
  • max-устаревший†
  • min-fresh
  • must-revalidate
  • no-cache
  • no-store
  • только в случае кэширования
  • private
  • public
  • S-maxage
  • proxy-revalidate" ( Прокси-revalidate)
†Если не задано max-staleограничение, по промежуточному слоям не выполняется никаких действий.
proxy-revalidate имеет тот же эффект, что must-revalidateи .

Дополнительные сведения см. в статье RFC 9111: директивы запроса.
Pragma Заголовок Pragma: no-cache в запросе создает тот же эффект, что Cache-Control: no-cacheи . Этот заголовок переопределяется соответствующими директивами в заголовке Cache-Control , если он присутствует. Рекомендуется обеспечить обратную совместимость с HTTP/1.0.
Set-Cookie Ответ не кэширован, если заголовок существует. Любое ПО промежуточного слоя в конвейере обработки запросов, задающее один или несколько cookies, запрещает кэширование по промежуточному слоям ответа (например, cookieпоставщик TempData на основе ответа).
Vary Заголовок Vary используется для изменения кэшированного ответа по другому заголовку. Например, ответы кэша кодируются путем кодирования, включая Vary: Accept-Encoding заголовок, который кэширует ответы на запросы с заголовками Accept-Encoding: gzip и Accept-Encoding: text/plain отдельно. Ответ со значением заголовка * никогда не сохраняется.
Expires Ответ, который считается устаревшим по этому заголовку, не сохраняется или извлекается, если не переопределяется другими Cache-Control заголовками.
If-None-Match Полный ответ обслуживается из кэша, если значение не * равно, а ETag ответ не соответствует ни одному из указанных значений. В противном случае обслуживается ответ 304 (не изменено).
If-Modified-Since If-None-Match Если заголовок отсутствует, полный ответ обслуживается из кэша, если дата кэшированного ответа является более новой, чем указанное значение. В противном случае отдается ответ 304 — не изменен.
Date При обслуживании из кэша заголовок устанавливается по промежуточному слоям, Date если он не был указан в исходном ответе.
Content-Length При обслуживании из кэша заголовок устанавливается по промежуточному слоям, Content-Length если он не был указан в исходном ответе.
Age Заголовок, Age отправленный в исходном ответе, игнорируется. ПО промежуточного слоя вычисляет новое значение при обслуживании кэшированного ответа.

Кэширование учитывает директивы Cache-Control для запросов

ПО промежуточного слоя учитывает правила RFC 9111: кэширование HTTP (раздел 5.2. Cache-Control). Для выполнения допустимых Cache-Control заголовков, отправленных клиентом, требуется кэш. В соответствии со спецификацией клиент может выполнять запросы со no-cache значением заголовка и принудительно создавать новый ответ для каждого запроса. В настоящее время при использовании ПО промежуточного слоя отсутствует контроль над этим поведением кэширования, так как по промежуточному слоям соответствует официальной спецификации кэширования.

Дополнительные сведения о поведении кэширования см. в других функциях кэширования ASP.NET Core. См. следующие разделы:

Устранение неполадок

Если поведение кэширования не так ожидаемо, убедитесь, что ответы кэшируются и могут обслуживаться из кэша. Проверьте входящие заголовки запроса и исходящие заголовки ответа. Включите ведение журнала для отладки.

При тестировании и устранении неполадок с кэшированием браузер может задать заголовки запросов, влияющие на кэширование нежелательными способами. Например, браузер может задать Cache-Control заголовок no-cache или max-age=0 при обновлении страницы. Следующие средства могут явно задать заголовки запросов и предпочтительнее для тестирования кэширования:

Условия кэширования

  • Запрос должен привести к ответу сервера с кодом состояния 200 (ОК).
  • Метод запроса должен быть GET или HEAD.
  • В Startup.Configureразделе "Кэширование ответов" по промежуточному слоям необходимо поместить перед по промежуточному слоям, требующим кэширования. Дополнительные сведения см. в статье ПО промежуточного слоя ASP.NET Core.
  • Заголовок Authorization не должен присутствовать.
  • Cache-Control Параметры заголовка должны быть допустимыми, а ответ должен быть помечен public и не помечен private.
  • Заголовок Pragma: no-cache не должен присутствовать, если Cache-Control заголовок отсутствует, так как Cache-Control заголовок переопределяет Pragma заголовок при наличии.
  • Заголовок Set-Cookie не должен присутствовать.
  • Vary Параметры заголовка должны быть допустимыми и не равными *.
  • Значение Content-Length заголовка (если задано) должно соответствовать размеру текста ответа.
  • Не IHttpSendFileFeature используется.
  • Ответ не должен быть устаревшим Expires , как указано в заголовке и max-ages-maxage директивах кэша.
  • Буферизация ответов должна быть успешной. Размер ответа должен быть меньше настроенного или по умолчанию SizeLimit. Размер текста ответа должен быть меньше настроенного или по умолчанию MaximumBodySize.
  • Ответ должен быть кэшируемым в соответствии с RFC 9111: кэширование HTTP. Например, директива no-store не должна существовать в полях заголовка запроса или ответа. Дополнительные сведения см. в разделе RFC 9111: кэширование HTTP (раздел 3. Хранение ответов в кэшах ).

Примечание.

Система защиты от атак для создания безопасных маркеров для предотвращения атак межсайтовых запросов forgery (CSRF) задает Cache-Control значения и Pragma заголовки таким образом, чтобы ответы no-cache не кэшировались. Сведения об отключении маркеров защиты от элементов формы HTML см. в статье "Предотвращение атак межсайтовых запросов forgery (XSRF/CSRF) в ASP.NET Core.

Дополнительные ресурсы