Created
May 23, 2014 07:55
-
-
Save daniiiol/88724069d29537b11fcd to your computer and use it in GitHub Desktop.
This file contains hidden or 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
namespace Dani.Samples.Internet.WebApi.Filters.Actions | |
{ | |
using System; | |
using System.Linq; | |
using System.Collections.Concurrent; | |
using System.Net; | |
using System.Net.Http; | |
using System.Net.Http.Headers; | |
using System.Text; | |
using System.Web.Http.Controllers; | |
using System.Web.Http.Filters; | |
// Proof of Concept !!! | |
// ------------------------------ | |
// Test ob ETags für OData-Queries berücksichtigt werden kann | |
// TODO: 1 - Aus dem Proc-Modus in eine DB-Variante wechseln | |
// TODO: 2 - Allgemein E-Tag kann als Message-Handler bei der WebAPI eingebunden werden und nicht als ActionFilter | |
// TODO: 3 - Ressourcen-Invalidierung als ActionFilter sauber erstellen (rudimentär hier mit Param der DependendenyKeys) | |
public class EnableETag : ActionFilterAttribute | |
{ | |
//This could be from a network source e.g database | |
private static readonly ConcurrentDictionary<string, EntityTagHeaderValue> EtTagHeaderValues = new ConcurrentDictionary<string, EntityTagHeaderValue>(); | |
private readonly string _dependencyKeys; | |
public EnableETag() { } | |
public EnableETag(string dependencyKeys) | |
{ | |
_dependencyKeys = dependencyKeys; | |
} | |
public override void OnActionExecuting(HttpActionContext actionContext) | |
{ | |
if (!string.IsNullOrEmpty(_dependencyKeys)) // TODO: oder wenn es kein GET ist... | |
{ | |
foreach (var entityTagHeaderValue in EtTagHeaderValues) | |
{ | |
var splittedKey = entityTagHeaderValue.Key.Split('|'); | |
var result = string.Empty; | |
foreach (var dependencyKey in _dependencyKeys.Split('|')) | |
{ | |
result = splittedKey.FirstOrDefault(k => k.Equals(dependencyKey, StringComparison.InvariantCultureIgnoreCase)); | |
if (!string.IsNullOrEmpty(result)) | |
{ | |
EntityTagHeaderValue value; | |
EtTagHeaderValues.TryRemove(entityTagHeaderValue.Key, out value); | |
break; | |
} | |
} | |
} | |
} | |
//Get the request | |
var request = actionContext.Request; | |
if (request.Method == HttpMethod.Get) | |
{ | |
var key = GetKey(actionContext); | |
//Get if If-None match header | |
var clientEtags = request.Headers.IfNoneMatch; | |
if (clientEtags.Count > 0) | |
{ | |
EntityTagHeaderValue etag = null; | |
if (EtTagHeaderValues.TryGetValue(key, out etag) && clientEtags.Any(t => t.Tag == etag.Tag)) | |
{ | |
actionContext.Response = new HttpResponseMessage(HttpStatusCode.NotModified); | |
SetCacheControl(actionContext.Response); | |
} | |
} | |
} | |
} | |
private void SetCacheControl(HttpResponseMessage httpResponseMessage) | |
{ | |
httpResponseMessage.Headers.CacheControl = new CacheControlHeaderValue() | |
{ | |
MaxAge = TimeSpan.FromSeconds(6), | |
MustRevalidate = true, | |
Private = true | |
}; | |
} | |
private string GetKey(HttpActionContext actionContext) | |
{ | |
var request = actionContext.Request; | |
string uri = string.Empty; | |
if (request.RequestUri.AbsoluteUri.Contains('?')) | |
{ | |
uri = request.RequestUri.AbsoluteUri.Substring(request.RequestUri.AbsoluteUri.IndexOf('?')); | |
} | |
var uriArray = uri.Split('&'); | |
var expand = uriArray.FirstOrDefault(segment => segment.StartsWith("$expand=", StringComparison.InvariantCultureIgnoreCase)); | |
var sbExpand = new StringBuilder(); | |
if (expand != null) | |
{ | |
var expandEntities = expand.Split(new[] { '/', ',' }, StringSplitOptions.RemoveEmptyEntries); | |
foreach (var expandEntity in expandEntities) | |
{ | |
sbExpand.Append(expandEntity); | |
sbExpand.Append("|"); | |
} | |
} | |
sbExpand.Append(actionContext.ControllerContext.ControllerDescriptor.ControllerName); | |
return sbExpand.ToString(); | |
} | |
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) | |
{ | |
var request = actionExecutedContext.Request; | |
var key = GetKey(actionExecutedContext.ActionContext); | |
EntityTagHeaderValue entityTag; | |
if (!EtTagHeaderValues.TryGetValue(key, out entityTag) || request.Method == HttpMethod.Post || request.Method == HttpMethod.Post) | |
{ | |
entityTag = new EntityTagHeaderValue("\"" + Guid.NewGuid().ToString() + "\""); | |
EtTagHeaderValues.AddOrUpdate(key, entityTag, (k, val) => entityTag); | |
} | |
actionExecutedContext.Response.Headers.ETag = entityTag; | |
SetCacheControl(actionExecutedContext.Response); | |
} | |
} | |
public class CacheKey | |
{ | |
public string Headers { get; set; } | |
public string Identifiers { get; set; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment