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

1

10

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?

Majorette answered 4/10, 2012 at 11:19 Comment(1)
For static content, you can use this: mike-ward.net/2014/01/13/…Salliesallow
M
14

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.

Majorette answered 9/11, 2012 at 10:41 Comment(1)
I don't think the .AfterRequest extension will be entered for static content.Salliesallow

© 2022 - 2024 — McMap. All rights reserved.