Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

extension for including ETag in responses #557

Closed
wants to merge 2 commits into from

2 participants

Prabir Shrestha Steven Robbins
Prabir Shrestha

Adds ETag header if it does not exist. If it already exists, it does not modify the existing ETag header value.

using Nancy.Caching;

public SampleModule()
{
    this.IncludeETag();
    Get["/"] = x => Response.AsJson(new { name = "Nancy" });
}

ETag value is generated using the md5 hash of the response body.

Pull request for #183

It is meant to go with pull request #547 (Conditional e-tags).

public SampleModule()
{
    this.CheckForIfNonMatch();
    this.IncludeETag();
    Get["/"] = x => Response.AsJson(new { name = "Nancy" });
}

This sample would return 304 not modified without the body if etag matches and will also always include the ETag header in the response.
@gissues:{"order":66.66666666666666,"status":"backlog"}

Steven Robbins
Owner

Will probably roll this into #183

Steven Robbins grumpydev closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
1  src/Nancy.Tests/Nancy.Tests.csproj
View
@@ -110,6 +110,7 @@
<Compile Include="Fakes\Person.cs" />
<Compile Include="Fakes\ViewModel.cs" />
<Compile Include="Unit\Bootstrapper\PipelinesFixture.cs" />
+ <Compile Include="Unit\Caching\ModuleExtensionsFixture.cs" />
<Compile Include="Unit\Conventions\DefaultStaticContentsConventionsFixture.cs" />
<Compile Include="Unit\DefaultNancyBootstrapperFixture.cs" />
<Compile Include="Unit\DefaultResponseFormatterFactoryFixture.cs" />
68 src/Nancy.Tests/Unit/Caching/ModuleExtensionsFixture.cs
View
@@ -0,0 +1,68 @@
+
+namespace Nancy.Tests.Unit.Caching
+{
+ using Nancy.Caching;
+ using Nancy.Tests.Fakes;
+ using Xunit;
+
+ public class ModuleExtensionsFixture
+ {
+ [Fact]
+ public void IncludeETag_should_not_override_the_etag_if_it_is_already_set_in_the_response()
+ {
+ // Given, when
+ var module = new FakeHookedModule(after: new AfterPipeline());
+ module.IncludeETag();
+
+ var context = new NancyContext
+ {
+ Request = new FakeRequest("GET", "/"),
+ Response = ((Response)"text").WithHeader("ETag", "dummy_etag")
+ };
+
+ module.After.Invoke(context);
+
+ // Then
+ context.Response.Headers["ETag"].ShouldEqual("dummy_etag");
+ }
+
+ [Fact]
+ public void IncludeETag_should_set_the_etag_if_it_does_not_exist_in_the_response()
+ {
+ // Given, when
+ var module = new FakeHookedModule(after: new AfterPipeline());
+ module.IncludeETag();
+
+ var context = new NancyContext
+ {
+ Request = new FakeRequest("GET", "/"),
+ Response = "text"
+ };
+
+ module.After.Invoke(context);
+
+ // Then
+ context.Response.Headers["ETag"].ShouldEqual("1CB251EC0D568DE6A929B520C4AED8D1");
+ }
+
+ [Fact]
+ public void IncludeETag_should_set_the_etag_if_response_is_empty()
+ {
+ // Given, when
+ var module = new FakeHookedModule(after: new AfterPipeline());
+ module.IncludeETag();
+
+ var context = new NancyContext
+ {
+ Request = new FakeRequest("GET", "/"),
+ Response = string.Empty
+ };
+
+ module.After.Invoke(context);
+
+ // Then
+ context.Response.Headers["ETag"].ShouldEqual("D41D8CD98F00B204E9800998ECF8427E");
+ }
+
+ }
+}
53 src/Nancy/Caching/ModuleExtensions.cs
View
@@ -0,0 +1,53 @@
+
+namespace Nancy.Caching
+{
+ using System.IO;
+ using System.Security.Cryptography;
+ using System.Text;
+
+ public static class ModuleExtensions
+ {
+ public static void IncludeETag(this NancyModule module)
+ {
+ module.After.AddItemToEndOfPipeline(IncludeETag);
+ }
+
+ private static void IncludeETag(NancyContext context)
+ {
+ var response = context.Response;
+
+ if (response.Headers.ContainsKey("ETag"))
+ {
+ return;
+ }
+
+ var ms = new MemoryStream();
+ response.Contents(ms);
+ var data = ms.ToArray();
+ ms.Seek(0, SeekOrigin.Begin);
+ response.Contents = ms.CopyTo;
+
+ response.Headers["ETag"] = GenerateETag(data);
+ }
+
+ private static string GenerateETag(byte[] data)
+ {
+ using (var md5 = MD5.Create())
+ {
+ var hash = md5.ComputeHash(data);
+ return ByteArrayToString(hash);
+ }
+ }
+
+ private static string ByteArrayToString(byte[] data)
+ {
+ var output = new StringBuilder(data.Length);
+ for (int i = 0; i < data.Length; i++)
+ {
+ output.Append(data[i].ToString("X2"));
+ }
+
+ return output.ToString();
+ }
+ }
+}
1  src/Nancy/Nancy.csproj
View
@@ -96,6 +96,7 @@
<Compile Include="Bootstrapper\IStartup.cs" />
<Compile Include="Bootstrapper\NancyInternalConfiguration.cs" />
<Compile Include="Bootstrapper\Pipelines.cs" />
+ <Compile Include="Caching\ModuleExtensions.cs" />
<Compile Include="Conventions\DefaultStaticContentsConventions.cs" />
<Compile Include="Conventions\DefaultViewLocationConventions.cs" />
<Compile Include="Conventions\IConvention.cs" />
Something went wrong with that request. Please try again.