Question

I'm trying to implement an output cache in WebApi that can cache the responses already processed by filters and generate responses that are not processed by formatters.

From what I've seen ActionFilterAttribute's OnActionExecuting and OnActionExecuted are execute before the serialization formatters and so if you cache a response, at a cache hit you will respond the exact same content and that content will be serialized again to transmission.

As a possible solution in MVC I think that you can do this by implementing a IResultFilter that override OnResultExecuted by caching the serialized response. With this approach I don't know how to intercept the request handling to avoid the serialization formatters, I think that a possible solution to intercept is create a custom ActionResult to be handled directly by IResultFilter. Please note that this solution is not suitable for me because I'm implementing OutputCache in a WebApi application.

Was it helpful?

Solution

While writing the response, formatters in Web API come into action for HttpContents of type ObjectContent only.

In you OnActionExecuted method, you could force the serialization to happen by doing something like below and then set the response content as StreamContent (this way the formatters wouldn't be coming into picture):

An example below:

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
    HttpResponseMessage response = actionExecutedContext.Response;

    if (response != null && response.IsSuccessStatusCode)
    {
        ObjectContent originalContent = response.Content as ObjectContent;

        if (originalContent != null)
        {
            MemoryStream ms = new MemoryStream();

            // NOTE:
            // 1. We are forcing serialization to occur into a buffered stream here
            // 2. This can cause exception. You can leave it as it is and Web API's exception handling mechanism should
            //    do the right thing.
            originalContent.CopyToAsync(ms).Wait();

            // reset the position
            ms.Position = 0;

            StreamContent newContent = new StreamContent(ms);

            // Copy the headers
            foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
            {
                newContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }

            //dispose the original content
            originalContent.Dispose();

            //TODO: cache this new httpcontent 'newContent' (so we are caching both serialized body + headers too)

            //Set the response
            //NOTE: This newContent will not hit the formatters
            actionExecutedContext.ActionContext.Response.Content = newContent;
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top