Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does NancyFX support static content caching via the ETag and Last-Modified headers?

I want my static content (images, javascript files, css files etc) to served in full only after the file has been updated.

If a file has not changed since it was last requested (as determined by the ETag and Last-Modified response header values) then I want the cached versions of the files to be used by the client browser.

Does Nancy support this functionality?

like image 502
biofractal Avatar asked Oct 04 '12 11:10

biofractal


People also ask

How does ETag caching work?

The ETag (or Entity Tag) works in a similar way to the Last-Modified header except its value is a digest of the resources contents (for instance, an MD5 hash). This allows the server to identify if the cached contents of the resource are different to the most recent version.

How do you cache static resources using HTTP caching?

Here is what you need to remember while caching static resources on CDN or local cache server: Use Cache-control HTTP directive to control who can cache the response, under which conditions, and for how long. Configure your server or application to send validation token Etag. Do not cache HTML in the browser.

Which headers can potentially allow a resource to be loaded from cache?

Cache-control is an HTTP header used to specify browser caching policies in both client requests and server responses. Policies include how a resource is cached, where it's cached and its maximum age before expiring (i.e., time to live).

Which HTTP caching header is used to avoid making requests to the origin server?

Expiration is the caching mechanism by which a client can entirely avoid making requests to the origin server. When the origin server specifies an explicit expiration time in the resource, a cache can check that expiration time and respond accordingly without having to contact the server first.


1 Answers

Nancy does partially support the ETag and the Last-Modified headers. It sets them for all static files but as of version 0.13 it does nothing with these values. here is the Nancy code:

Nancy.Responses.GenericFileResponse.cs

if (IsSafeFilePath(rootPath, fullPath))
{
    Filename = Path.GetFileName(fullPath);

    var fi = new FileInfo(fullPath);
    // TODO - set a standard caching time and/or public?
    Headers["ETag"] = fi.LastWriteTimeUtc.Ticks.ToString("x");
    Headers["Last-Modified"] = fi.LastWriteTimeUtc.ToString("R");
    Contents = GetFileContent(fullPath);
    ContentType = contentType;
    StatusCode = HttpStatusCode.OK;
    return;
}

To make use of the ETag and Last-Modified header values you need to add a couple of modified extensions methods. I borrowed these directly from the Nancy source code in GitHub (as this functionality is planned for a future release) but the original idea came from Simon Cropp - Conditional responses with NancyFX

Extension Methods

public static void CheckForIfNonMatch(this NancyContext context)
{
    var request = context.Request;
    var response = context.Response;

    string responseETag;
    if (!response.Headers.TryGetValue("ETag", out responseETag)) return;
    if (request.Headers.IfNoneMatch.Contains(responseETag))
    {
        context.Response = HttpStatusCode.NotModified;
    }
}

public static void CheckForIfModifiedSince(this NancyContext context)
{
    var request = context.Request;
    var response = context.Response;

    string responseLastModified;
    if (!response.Headers.TryGetValue("Last-Modified", out responseLastModified)) return;
    DateTime lastModified;

    if (!request.Headers.IfModifiedSince.HasValue || !DateTime.TryParseExact(responseLastModified, "R", CultureInfo.InvariantCulture, DateTimeStyles.None, out lastModified)) return;
    if (lastModified <= request.Headers.IfModifiedSince.Value)
    {
        context.Response = HttpStatusCode.NotModified;
    }
}

Finally you need to call these methods using the AfterRequest hook in your Nancy BootStrapper.

BootStrapper

public class MyBootstrapper :DefaultNancyBootstrapper
{
    protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
    {
        pipelines.AfterRequest += ctx =>
        {
            ctx.CheckForIfNoneMatch();
            ctx.CheckForIfModifiedSince();
        };
        base.ApplicationStartup(container, pipelines);
    }
    //more stuff
}

Watching the responses with Fiddler you will see the first hit to your static files downloads them with a 200 - OK Status Code.

Thereafter each request returns a 304 - Not Modified Status Code. After a file is updated, requesting it once again downloads it with a 200 - OK Status Code ... and so on.

like image 99
biofractal Avatar answered Sep 21 '22 23:09

biofractal