Skip to content

Commit 68096a1

Browse files
committed
Merge pull request #140 from filipw/dev
Release 0.8.0
2 parents ae27876 + 37ab39a commit 68096a1

16 files changed

Lines changed: 161 additions & 113 deletions

src/WebApi.OutputCache.Core/Cache/IApiOutputCache.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,18 @@ namespace WebApi.OutputCache.Core.Cache
66
public interface IApiOutputCache
77
{
88
void RemoveStartsWith(string key);
9+
910
T Get<T>(string key) where T : class;
11+
12+
[Obsolete("Use Get<T> instead")]
1013
object Get(string key);
14+
1115
void Remove(string key);
16+
1217
bool Contains(string key);
18+
1319
void Add(string key, object o, DateTimeOffset expiration, string dependsOnKey = null);
20+
1421
IEnumerable<string> AllKeys { get; }
1522
}
1623
}

src/WebApi.OutputCache.Core/Cache/MemoryCacheDefault.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public T Get<T>(string key) where T : class
2323
return o;
2424
}
2525

26+
[Obsolete("Use Get<T> instead")]
2627
public object Get(string key)
2728
{
2829
return Cache.Get(key);

src/WebApi.OutputCache.V2/AutoInvalidateCacheOutputAttribute.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ public override void OnActionExecuted(HttpActionExecutedContext actionExecutedCo
2020
if (actionExecutedContext.Response != null && !actionExecutedContext.Response.IsSuccessStatusCode) return;
2121
if (actionExecutedContext.ActionContext.Request.Method != HttpMethod.Post &&
2222
actionExecutedContext.ActionContext.Request.Method != HttpMethod.Put &&
23-
actionExecutedContext.ActionContext.Request.Method != HttpMethod.Delete) return;
23+
actionExecutedContext.ActionContext.Request.Method != HttpMethod.Delete &&
24+
actionExecutedContext.ActionContext.Request.Method.Method.ToLower() != "patch" &&
25+
actionExecutedContext.ActionContext.Request.Method.Method.ToLower() != "merge") return;
2426

2527
var controller = actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor;
2628
var actions = FindAllGetMethods(controller.ControllerType, TryMatchType ? actionExecutedContext.ActionContext.ActionDescriptor.GetParameters() : null);
@@ -30,7 +32,7 @@ public override void OnActionExecuted(HttpActionExecutedContext actionExecutedCo
3032

3133
foreach (var action in actions)
3234
{
33-
var key = config.CacheOutputConfiguration().MakeBaseCachekey(controller.ControllerName, action);
35+
var key = config.CacheOutputConfiguration().MakeBaseCachekey(controller.ControllerType.FullName, action);
3436
if (WebApiCache.Contains(key))
3537
{
3638
WebApiCache.RemoveStartsWith(key);

src/WebApi.OutputCache.V2/CacheOutputAttribute.cs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ namespace WebApi.OutputCache.V2
2020
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
2121
public class CacheOutputAttribute : ActionFilterAttribute
2222
{
23+
private const string CurrentRequestMediaType = "CacheOutput:CurrentRequestMediaType";
2324
protected static MediaTypeHeaderValue DefaultMediaType = new MediaTypeHeaderValue("application/json") {CharSet = Encoding.UTF8.HeaderName};
2425

2526
/// <summary>
@@ -62,8 +63,6 @@ public class CacheOutputAttribute : ActionFilterAttribute
6263
/// </summary>
6364
public Type CacheKeyGenerator { get; set; }
6465

65-
private MediaTypeHeaderValue _responseMediaType;
66-
6766
// cache repository
6867
private IApiOutputCache _webApiCache;
6968

@@ -100,9 +99,15 @@ protected virtual MediaTypeHeaderValue GetExpectedMediaType(HttpConfiguration co
10099
var negotiator = config.Services.GetService(typeof(IContentNegotiator)) as IContentNegotiator;
101100
var returnType = actionContext.ActionDescriptor.ReturnType;
102101

103-
if (negotiator != null && returnType != typeof(HttpResponseMessage))
102+
if (negotiator != null && returnType != typeof(HttpResponseMessage) && (returnType != typeof(IHttpActionResult) || typeof(IHttpActionResult).IsAssignableFrom(returnType)))
104103
{
105104
var negotiatedResult = negotiator.Negotiate(returnType, actionContext.Request, config.Formatters);
105+
106+
if (negotiatedResult == null)
107+
{
108+
return DefaultMediaType;
109+
}
110+
106111
responseMediaType = negotiatedResult.MediaType;
107112
if (string.IsNullOrWhiteSpace(responseMediaType.CharSet))
108113
{
@@ -114,8 +119,7 @@ protected virtual MediaTypeHeaderValue GetExpectedMediaType(HttpConfiguration co
114119
if (actionContext.Request.Headers.Accept != null)
115120
{
116121
responseMediaType = actionContext.Request.Headers.Accept.FirstOrDefault();
117-
if (responseMediaType == null ||
118-
!config.Formatters.Any(x => x.SupportedMediaTypes.Contains(responseMediaType)))
122+
if (responseMediaType == null || !config.Formatters.Any(x => x.SupportedMediaTypes.Contains(responseMediaType)))
119123
{
120124
return DefaultMediaType;
121125
}
@@ -138,14 +142,15 @@ public override void OnActionExecuting(HttpActionContext actionContext)
138142

139143
var cacheKeyGenerator = config.CacheOutputConfiguration().GetCacheKeyGenerator(actionContext.Request, CacheKeyGenerator);
140144

141-
_responseMediaType = GetExpectedMediaType(config, actionContext);
142-
var cachekey = cacheKeyGenerator.MakeCacheKey(actionContext, _responseMediaType, ExcludeQueryStringFromCacheKey);
145+
var responseMediaType = GetExpectedMediaType(config, actionContext);
146+
actionContext.Request.Properties[CurrentRequestMediaType] = responseMediaType;
147+
var cachekey = cacheKeyGenerator.MakeCacheKey(actionContext, responseMediaType, ExcludeQueryStringFromCacheKey);
143148

144149
if (!_webApiCache.Contains(cachekey)) return;
145150

146151
if (actionContext.Request.Headers.IfNoneMatch != null)
147152
{
148-
var etag = _webApiCache.Get(cachekey + Constants.EtagKey) as string;
153+
var etag = _webApiCache.Get<string>(cachekey + Constants.EtagKey);
149154
if (etag != null)
150155
{
151156
if (actionContext.Request.Headers.IfNoneMatch.Any(x => x.Tag == etag))
@@ -159,16 +164,16 @@ public override void OnActionExecuting(HttpActionContext actionContext)
159164
}
160165
}
161166

162-
var val = _webApiCache.Get(cachekey) as byte[];
167+
var val = _webApiCache.Get<byte[]>(cachekey);
163168
if (val == null) return;
164169

165-
var contenttype = _webApiCache.Get(cachekey + Constants.ContentTypeKey) as MediaTypeHeaderValue ?? new MediaTypeHeaderValue(cachekey.Split(new[] {':'},2)[1]);
170+
var contenttype = _webApiCache.Get<MediaTypeHeaderValue>(cachekey + Constants.ContentTypeKey) ?? new MediaTypeHeaderValue(cachekey.Split(new[] {':'},2)[1]);
166171

167172
actionContext.Response = actionContext.Request.CreateResponse();
168173
actionContext.Response.Content = new ByteArrayContent(val);
169174

170175
actionContext.Response.Content.Headers.ContentType = contenttype;
171-
var responseEtag = _webApiCache.Get(cachekey + Constants.EtagKey) as string;
176+
var responseEtag = _webApiCache.Get<string>(cachekey + Constants.EtagKey);
172177
if (responseEtag != null) SetEtag(actionContext.Response, responseEtag);
173178

174179
var cacheTime = CacheTimeQuery.Execute(DateTime.Now);
@@ -184,10 +189,12 @@ public override async Task OnActionExecutedAsync(HttpActionExecutedContext actio
184189
var cacheTime = CacheTimeQuery.Execute(DateTime.Now);
185190
if (cacheTime.AbsoluteExpiration > DateTime.Now)
186191
{
187-
var config = actionExecutedContext.Request.GetConfiguration().CacheOutputConfiguration();
192+
var httpConfig = actionExecutedContext.Request.GetConfiguration();
193+
var config = httpConfig.CacheOutputConfiguration();
188194
var cacheKeyGenerator = config.GetCacheKeyGenerator(actionExecutedContext.Request, CacheKeyGenerator);
189195

190-
var cachekey = cacheKeyGenerator.MakeCacheKey(actionExecutedContext.ActionContext, _responseMediaType, ExcludeQueryStringFromCacheKey);
196+
var responseMediaType = actionExecutedContext.Request.Properties[CurrentRequestMediaType] as MediaTypeHeaderValue ?? GetExpectedMediaType(httpConfig, actionExecutedContext.ActionContext);
197+
var cachekey = cacheKeyGenerator.MakeCacheKey(actionExecutedContext.ActionContext, responseMediaType, ExcludeQueryStringFromCacheKey);
191198

192199
if (!string.IsNullOrWhiteSpace(cachekey) && !(_webApiCache.Contains(cachekey)))
193200
{
@@ -197,7 +204,7 @@ public override async Task OnActionExecutedAsync(HttpActionExecutedContext actio
197204

198205
if (responseContent != null)
199206
{
200-
var baseKey = config.MakeBaseCachekey(actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName, actionExecutedContext.ActionContext.ActionDescriptor.ActionName);
207+
var baseKey = config.MakeBaseCachekey(actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerType.FullName, actionExecutedContext.ActionContext.ActionDescriptor.ActionName);
201208
var contentType = responseContent.Headers.ContentType;
202209
string etag = actionExecutedContext.Response.Headers.ETag.Tag;
203210
//ConfigureAwait false to avoid deadlocks

src/WebApi.OutputCache.V2/CacheOutputConfiguration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public string MakeBaseCachekey<T, U>(Expression<Func<T, U>> expression)
5454
}
5555
}
5656

57-
return string.Format("{0}-{1}", typeof(T).Name.Replace("Controller",string.Empty).ToLower(), methodName.ToLower());
57+
return string.Format("{0}-{1}", typeof(T).FullName.ToLower(), methodName.ToLower());
5858
}
5959

6060
private static ICacheKeyGenerator TryActivateCacheKeyGenerator(Type generatorType)

src/WebApi.OutputCache.V2/DefaultCacheKeyGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class DefaultCacheKeyGenerator : ICacheKeyGenerator
1313
{
1414
public virtual string MakeCacheKey(HttpActionContext context, MediaTypeHeaderValue mediaType, bool excludeQueryString = false)
1515
{
16-
var controller = context.ControllerContext.ControllerDescriptor.ControllerName;
16+
var controller = context.ControllerContext.ControllerDescriptor.ControllerType.FullName;
1717
var action = context.ActionDescriptor.ActionName;
1818
var key = context.Request.GetConfiguration().CacheOutputConfiguration().MakeBaseCachekey(controller, action);
1919
var actionParameters = context.ActionArguments.Where(x => x.Value != null).Select(x => x.Key + "=" + GetValue(x.Value));

src/WebApi.OutputCache.V2/InvalidateCacheOutputAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public InvalidateCacheOutputAttribute(string methodName, Type type = null)
2424
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
2525
{
2626
if (actionExecutedContext.Response != null && !actionExecutedContext.Response.IsSuccessStatusCode) return;
27-
_controller = _controller ?? actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName;
27+
_controller = _controller ?? actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerType.FullName;
2828

2929
var config = actionExecutedContext.Request.GetConfiguration();
3030
EnsureCache(config, actionExecutedContext.Request);

test/WebApi.OutputCache.V2.Tests/CacheKeyGeneratorRegistrationTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public void selected_generator_with_internal_registration_is_used()
8989
var client = new HttpClient(_server);
9090
var result = client.GetAsync(_url + "cachekey/get_internalregistered").Result;
9191

92-
_cache.Verify(s => s.Add(It.Is<string>(x => x == "internal"), It.IsAny<byte[]>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), It.Is<string>(x => x == "cachekey-get_internalregistered")), Times.Once());
92+
_cache.Verify(s => s.Add(It.Is<string>(x => x == "internal"), It.IsAny<byte[]>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), It.Is<string>(x => x == "webapi.outputcache.v2.tests.testcontrollers.cachekeycontroller-get_internalregistered")), Times.Once());
9393
}
9494

9595
[Test]

test/WebApi.OutputCache.V2.Tests/CacheKeyGeneratorTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ public void custom_default_cache_key_generator_called_and_key_used()
6666
var result = client.GetAsync(_url + "sample/Get_c100_s100").Result;
6767

6868
_cache.Verify(s => s.Contains(It.Is<string>(x => x == "keykeykey")), Times.Exactly(2));
69-
_cache.Verify(s => s.Add(It.Is<string>(x => x == "keykeykey"), It.IsAny<byte[]>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), It.Is<string>(x => x == "sample-get_c100_s100")), Times.Once());
70-
_cache.Verify(s => s.Add(It.Is<string>(x => x == "keykeykey:response-ct"), It.IsAny<object>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), It.Is<string>(x => x == "sample-get_c100_s100")), Times.Once());
69+
_cache.Verify(s => s.Add(It.Is<string>(x => x == "keykeykey"), It.IsAny<byte[]>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), It.Is<string>(x => x == "webapi.outputcache.v2.tests.testcontrollers.samplecontroller-get_c100_s100")), Times.Once());
70+
_cache.Verify(s => s.Add(It.Is<string>(x => x == "keykeykey:response-ct"), It.IsAny<object>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), It.Is<string>(x => x == "webapi.outputcache.v2.tests.testcontrollers.samplecontroller-get_c100_s100")), Times.Once());
7171

7272
_keyGeneratorA.VerifyAll();
7373
}
@@ -79,8 +79,8 @@ public void custom_cache_key_generator_called()
7979
var result = client.GetAsync(_url + "cachekey/get_custom_key").Result;
8080

8181
_cache.Verify(s => s.Contains(It.Is<string>(x => x == "custom_key")), Times.Exactly(2));
82-
_cache.Verify(s => s.Add(It.Is<string>(x => x == "custom_key"), It.IsAny<byte[]>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), It.Is<string>(x => x == "cachekey-get_custom_key")), Times.Once());
83-
_cache.Verify(s => s.Add(It.Is<string>(x => x == "custom_key:response-ct"), It.IsAny<object>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), It.Is<string>(x => x == "cachekey-get_custom_key")), Times.Once());
82+
_cache.Verify(s => s.Add(It.Is<string>(x => x == "custom_key"), It.IsAny<byte[]>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), It.Is<string>(x => x == "webapi.outputcache.v2.tests.testcontrollers.cachekeycontroller-get_custom_key")), Times.Once());
83+
_cache.Verify(s => s.Add(It.Is<string>(x => x == "custom_key:response-ct"), It.IsAny<object>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), It.Is<string>(x => x == "webapi.outputcache.v2.tests.testcontrollers.cachekeycontroller-get_custom_key")), Times.Once());
8484
}
8585
}
8686
}

test/WebApi.OutputCache.V2.Tests/ConfigurationTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ public void cache_singleton_in_pipeline()
3333
var client = new HttpClient(_server);
3434
var result = client.GetAsync(_url + "Get_c100_s100").Result;
3535

36-
_cache.Verify(s => s.Contains(It.Is<string>(x => x == "sample-get_c100_s100:application/json; charset=utf-8")), Times.Exactly(2));
36+
_cache.Verify(s => s.Contains(It.Is<string>(x => x == "webapi.outputcache.v2.tests.testcontrollers.samplecontroller-get_c100_s100:application/json; charset=utf-8")), Times.Exactly(2));
3737

3838
var result2 = client.GetAsync(_url + "Get_c100_s100").Result;
39-
_cache.Verify(s => s.Contains(It.Is<string>(x => x == "sample-get_c100_s100:application/json; charset=utf-8")), Times.Exactly(4));
39+
_cache.Verify(s => s.Contains(It.Is<string>(x => x == "webapi.outputcache.v2.tests.testcontrollers.samplecontroller-get_c100_s100:application/json; charset=utf-8")), Times.Exactly(4));
4040

4141
_server.Dispose();
4242
}

0 commit comments

Comments
 (0)