Options

edit

HttpCachingOptions configures a HttpCachingDelegateHandler. Pass it as the last argument of the constructor:

var options = new HttpCachingOptions
{
    // Do not store the responses larger than 1 MB. The default is 5 MB, and null removes the limit.
    MaximumResponseSize = 1024 * 1024,

    // Only store the successful responses, even when the server allows storing the others
    ShouldCacheResponse = response => response.IsSuccessStatusCode,
};
OptionDefaultWhat it does
MaximumResponseSize5 MB (5,242,880 bytes)The largest response stored, in bytes. null removes the limit
ShouldCacheResponsenullA predicate that can prevent storing a response
TimeProviderTimeProvider.SystemThe clock used to compute the age and the freshness of the responses
OnStoreErrornullA callback invoked when the store fails

Limit the size of the stored responses

The cache reads the body of a response to store it, so a large response costs memory and space in the store. MaximumResponseSize bounds that:

  • A response whose Content-Length is larger than the limit is not read by the cache at all.
  • A response without Content-Length is read up to the limit, then left unstored.
  • The limit applies to the stored entry, which holds the headers and an encoded copy of the body: a body slightly smaller than the limit can still be too large.

In every case, the caller receives the full response. Only the storage is skipped.

Choose which responses to store

ShouldCacheResponse is called for each response the HTTP caching rules allow to store. When it returns false, the response is not stored. It cannot make the cache store a response that the rules do not allow, such as one with Cache-Control: no-store.

Control the clock

The cache computes the age and the freshness of the responses with TimeProvider. Replace it with a FakeTimeProvider to test how your application behaves when responses become stale, without waiting:

// FakeTimeProvider comes from the Microsoft.Extensions.TimeProvider.Testing package
var timeProvider = new FakeTimeProvider();
var options = new HttpCachingOptions
{
    TimeProvider = timeProvider,
};

using var cachingHandler = new HttpCachingDelegateHandler(originHandler, new InMemoryHttpCacheStore(), options);
using var httpClient = new HttpClient(cachingHandler);

// The server answers with Cache-Control: max-age=300
Assert.Equal("v1", await httpClient.GetStringAsync("https://example.com/data"));

// Five minutes later, the stored response is stale, so the cache asks the server again
timeProvider.Advance(TimeSpan.FromMinutes(5));
Assert.Equal("v2", await httpClient.GetStringAsync("https://example.com/data"));

PruneObsoleteEntriesAsync does not use this clock: pass the time to the overload that takes one, PruneObsoleteEntriesAsync(timeProvider.GetUtcNow()). See Cache stores.

Observe store failures

A failing IHttpCacheStore never fails the request. A lookup that throws is treated as a cache miss, and a write that throws is skipped, so a Redis outage or a locked SQLite file degrades to no caching rather than to a broken HttpClient. Set OnStoreError to log these failures:

var options = new HttpCachingOptions
{
    OnStoreError = ex => logger.LogWarning(ex, "HTTP cache store failure"),
};

Without it, the failures are silent. A cancellation requested by the caller is not a store failure: it still cancels the request.