OutputCache: expose `OutputCacheStream ` for reading in `ServeResponseAsync(..)` so response tags can be built
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
}
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.