Caching without compression

I have a problem with setting up caching/compression for my service.

I am trying to cache service response + write corresponding HTTP caching headers, with control over response compression (to be able to enable/disable it up to my needs, not using it for e.g. images).

As I need control over returned content type (will return binary files with various types) I must to be able to set content type manually. Additionally, I want to cache 404 responses (returned for null view models).

And yes - mem caching here is intentional, files are small and needs to be cached.


Approach #1

I tried Request.ToOptimizedResultUsingCache approach but this didn’t work with HttpResponses created by me (as above - because of content type controll, 404s) and in compression being compulsory enabled.

Sample service:

public object Get(SampleRequest request)
{
    return Request.ToOptimizedResultUsingCache(Cache, Request.RawUrl, TimeSpan.FromSeconds(60),
    () =>
    {
        return new HttpResult(new byte[] {1, 2, 3}, "image/jpeg");
    });
}

and this seems to ignore given content type, always returning text/html, always with compression enabled.


Approach #2

I found out brand new CacheResponse annotation support - updated to 4.0.60 and enabled following plugin:

Plugins.Add(new HttpCacheFeature());

and here is sample service:

[CacheResponse(Duration = 123, MaxAge = 123, CacheControl = CacheControl.Public)]
public HttpResult Get(SampleRequest request)
{
    return new HttpResult("sample response");
}

what seems to be working… unless I specify content type in HttpResult. Than - all caching functionality stop to be applied.
Additionally, compression is always enabled. I dotpeeked the HttpCacheFeature and it seems like compression is enabled for each request being sent with accept-encoding header (what means - for each request nowadays).

Is there any way to cache service response without compression, with control over content type?

I’ve just added support to disable Compression with the NoCompression option in this commit which you can use with:

[CacheResponse(Duration=123, MaxAge=123, CacheControl=CacheControl.Public, NoCompression=true)]
public HttpResult Get(SampleRequest request)
{
    return new HttpResult("sample response");
}

You also don’t need to register HttpCacheFeature plugin as it’s registered by default.

This change is availble from v4.0.61 that’s now available on MyGet.

Thanks for the action. When can I expect the change to be shipped in official, regression tested, version?


And what about second issue - being able to specify content type manually?

The annotation works with

return new HttpResult("sample response");

whilst after adding content type

return new HttpResult("sample response", "text/html");

both server-side cache and HTTP headers stopped to be applied.

NuGet releases are around every ~6 weeks, the next release isn’t for a few weeks yet. But the pre-releases on MyGet goes the through the exact same CI test suite that the NuGet packages go through, there’s no difference between the MyGet and NuGet packages binaries other than when they’re released.

You can’t cache a Customized HttpResult, but you should be able to change what Content Type is cached and return using a Request Filter that changes the Content Type of the Service before the [CacheResponse] is executed e,g:

[AddHeader(ContentType = "text/html")]
[CacheResponse(Duration=123, MaxAge=123, CacheControl=CacheControl.Public, NoCompression=true)]
public object Get(SampleRequest request)
{
    return "sample response";
}

How to set Content-Type header dynamically (basing at the DTO I receive from lower layer) and to have this cached and returned to client?

I think this is very common case when one needs to set content type dynamically (e.g. for endpoint serving generated/fetched from storage images, documents) and to have output cache over there.

Normally the client requests the Content-Type using any of the standard content negotiation options, otherwise you can dynamically change the content type in a filter by setting:

req.ResponseContentType = "text/html";

But this needs to be run in before the [CacheResponse] filter is executed, so either in a Global Response Filter or a custom Request Filter Attribute.