Search code examples
asp.net-core

Response.RemoveOutputCacheItem in ASP.NET Core


In ASP.NET MVC 5 I often invalidated request cache items with code like this in a controller

Response.RemoveOutputCacheItem("/Client/Photo/1");

The RemoveOutputCacheItem option appears to be removed in ASP.NET Core. What is the alternative?


Solution

  • I worked this out, it is a multi step process.

    Firstly you need to create your own OutputCache Policy, something like this

    public class OutputCacheWithAuthPolicy : IOutputCachePolicy
    {
        public static readonly OutputCacheWithAuthPolicy Instance = new();
        private OutputCacheWithAuthPolicy() { }
    
        ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
        {
            var attemptOutputCaching = AttemptOutputCaching(context);
            context.EnableOutputCaching = true;
            context.AllowCacheLookup = attemptOutputCaching;
            context.AllowCacheStorage = attemptOutputCaching;
            context.AllowLocking = true;
    
            var id = context.HttpContext.Request.RouteValues["id"];
            if (id != null)
                context.Tags.Add($"Client_{id}");
    
            // Vary by any query by default
            context.CacheVaryByRules.QueryKeys = "*";
            return ValueTask.CompletedTask;
        }
        private static bool AttemptOutputCaching(OutputCacheContext context)
        {
            var request = context.HttpContext.Request;
    
            if (!HttpMethods.IsGet(request.Method) && !HttpMethods.IsHead(request.Method))
            {
                return false;
            }
    
            return true;
        }
        public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation) => ValueTask.CompletedTask;
        public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellation) => ValueTask.CompletedTask;
    }
    

    The important bit is where the tag with the ID is being added. This will be required to evict from the cache later on.

    In your Program.cs you will need to configure the policy like this

    builder.Services.AddOutputCache(options =>
    {
        options.AddBasePolicy(builder => builder.Cache());
        options.AddPolicy("OutputCacheWithAuthPolicy", OutputCacheWithAuthPolicy.Instance);
    })
    ....
    app.UseOutputCache();
    

    In my controller I have code something like this.

    public ClientController: Controller
    {
      public ClientController(IOutputCacheStore outputCache) 
      {
        _outputCache = outputCache;
      }
    
      private IOutputCacheStore _outputCache;
    
      [OutputCache(VaryByQueryKeys = ["id"], Duration = 86400, PolicyName = "OutputCacheWithAuthPolicy")]
      public IActionResult ProfilePic(int id)
      {
        var profilePic = GetPic();
        return File(profilePic, "image/png");
      }
    
      public async Task<IActionResult> EvictPic(int id, CancellationToken token)
      {
            await _outputCache.EvictByTagAsync($"Client_{id}", token);
            ....
      }
    }
    

    You will see the EvictPic action which forcibly evicts it from the cache