aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

OutputCache: expose `OutputCacheStream ` for reading in `ServeResponseAsync(..)` so response tags can be built

Open brettwinters opened this issue 1 year ago • 2 comments

Background and Motivation

For outout caching its often useful to be able to create tags based on the response. This occurs where different requests all touch the same underlying data and the tag representing this underlying data cannot be constructed based on the request data. For example, say, requests for a same user details based on UserId, Identity and EmailAddress.

https://api.com/userdetailsbyid?userid=1234
https://api.com/[email protected]
https://api.com/userdetailsbyidentity?identityprovider=https://idp.com&subject=abc

At the moment the only way to build the tags for these queries is to use the request data. The problem is that any command handlers would need to look up these details, and evict all these tags separately

await _outputCacheStore.EvictByTagAsync("Tag_User_by_userid=...);
await _outputCacheStore.EvictByTagAsync("Tag_User_by_emailaddress=...);
await _outputCacheStore.EvictByTagAsync("Tag_User_by_identity=...);
etc

What I'd like to do is to add a tag for each request in a custom policy by using the response which in my example contains the userid

await _outputCacheStore.EvictByTagAsync(
    "Tag_UserId_1234",
    CancellationToken.None
);

The workaround is to use reflection in the ServeResponseAsync(..) to get the value of the outputCacheContext.OutputCacheStream then invoke the GetCachedResponseBody() in the internal OutputCacheStream before reading the response (my policy is a generic typed policy so it's easy to deserialise and add the tags to the tags collection:

if (_responseTags.Count != 0)
{
     var responseBodyJson = await ExtractResponseBodyAsync(
	context: context, 
	cancellationToken: cancellationToken
    );

  var view = JsonConvert.DeserializeObject<TView>(responseBodyJson);

  if (view is not null)
  {
	  foreach (var tag in _responseTags)
	  {
		  context.Tags.Add(tag.Compile().Invoke(view));
	  }
  }
}

Then somehow write it back. Who knows what this breaks...

Proposed API

Make the OutputCacheStream property public or provide a public method to GetCachedResponseBody() from the OutputCacheContext in the ServeResponseAsync(...) method.

Usage Examples

public async ValueTask ServeResponseAsync(
	    OutputCacheContext context, 
	    CancellationToken cancellationToken)
    {
     var responseBody= await context.GetCachedResponseBody();
    //etc
}

brettwinters avatar Aug 26 '24 08:08 brettwinters

Have reported the post above to GitHub for posting what's likely malware. Received an identical comment from a different account in the Swashbuckle repo today as well.

martincostello avatar Aug 26 '24 08:08 martincostello