Created
July 1, 2011 23:52
-
-
Save howarddierking/1059599 to your computer and use it in GitHub Desktop.
sample message handler for managing etags in web api
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class EtagMessageHandler : DelegatingChannel | |
{ | |
readonly IETagStore _eTagStore; | |
public EtagMessageHandler(HttpMessageChannel innerChannel) : base(innerChannel) { | |
Trace.WriteLine("EtagMessageHandler - Ctor"); | |
_eTagStore = new InMemoryETagStore(); | |
} | |
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, | |
CancellationToken cancellationToken) { | |
Trace.WriteLine("EtagMessageHandler - SendAsync"); | |
var taskFactory = new TaskFactory<HttpResponseMessage>(); | |
if (request.Method == HttpMethod.Get) { | |
//if this is not a conditional get | |
if (request.Headers.IfNoneMatch.Count == 0) | |
return base.SendAsync(request, cancellationToken).ContinueWith(task => { | |
Trace.WriteLine("EtagMessageHandler - Task - Not a conditional GET"); | |
var resp = task.Result; | |
resp.Headers.ETag = new EntityTagHeaderValue(_eTagStore.Fetch(request.RequestUri)); | |
return resp; | |
}); | |
//if this is not modified, stop processing the message and return a 304; make sure the etag is also set | |
if (IfNoneMatchContainsStoredEtagValue(request)) | |
return taskFactory.StartNew(() => | |
{ | |
Trace.WriteLine("EtagMessageHandler - Task - Conditional GET match"); | |
var resp = new HttpResponseMessage(HttpStatusCode.NotModified, "Not Modified"); | |
resp.Headers.ETag = new EntityTagHeaderValue(_eTagStore.Fetch(request.RequestUri)); | |
return resp; | |
}); | |
//if this resource has been updated, let the request proceed to the operation and set a new etag on the return | |
return base.SendAsync(request, cancellationToken).ContinueWith(task => { | |
Trace.WriteLine("EtagMessageHandler - Task - Conditional GET not matched"); | |
var resp = task.Result; | |
resp.Headers.ETag = new EntityTagHeaderValue(_eTagStore.Fetch(request.RequestUri)); | |
return resp; | |
}); | |
} | |
if (request.Method == HttpMethod.Put || request.Method == HttpMethod.Post) | |
//let the request processing continue; new etag value for resource; update response header | |
if (IfMatchContainsStoredEtagValue(request)) | |
return base.SendAsync(request, cancellationToken).ContinueWith(task => { | |
Trace.WriteLine("EtagMessageHandler - Task - Conditional update matched"); | |
var resp = task.Result; | |
resp.Headers.ETag = new EntityTagHeaderValue(_eTagStore.UpdateETagFor(request.RequestUri)); | |
return resp; | |
}); | |
//stop processing and return a 412/precondition failed; update response header | |
else | |
return taskFactory.StartNew(() => { | |
Trace.WriteLine("EtagMessageHandler - Task - Conditional update not matched"); | |
var resp = new HttpResponseMessage(HttpStatusCode.PreconditionFailed, "Precondition Failed"); | |
resp.Headers.ETag = new EntityTagHeaderValue(_eTagStore.Fetch(request.RequestUri)); | |
return resp; | |
}); | |
//by default, let the request keep moving up the message handler stack | |
return base.SendAsync(request, cancellationToken); | |
} | |
bool IfNoneMatchContainsStoredEtagValue(HttpRequestMessage request) { | |
//we're going to assume that this handler will only care about 1 entity tag value for the specified resource | |
if (request.Headers.IfNoneMatch.Count == 0) | |
return false; | |
return request.Headers.IfNoneMatch.Select(v => v.Tag).Contains(_eTagStore.Fetch(request.RequestUri)); | |
} | |
bool IfMatchContainsStoredEtagValue(HttpRequestMessage request) { | |
if (request.Headers.IfMatch.Count == 0) | |
return false; | |
return request.Headers.IfMatch.Select(v => v.Tag).Contains(_eTagStore.Fetch(request.RequestUri)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment