Skip to content

Commit 6d528a6

Browse files
committed
Merge pull request filipw#144 from filipw/feature/cache-ignore
Added ability to opt out from caching
2 parents 29285da + ab9dfee commit 6d528a6

File tree

11 files changed

+118
-8
lines changed

11 files changed

+118
-8
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,29 @@ So you either should use unique method names inside a single controller, or (if
126126
}
127127

128128

129+
Ignoring caching
130+
--------------------
131+
You can set up caching globally (add the caching filter to `HttpConfiguration`) or on controller level (decorate the controller with the cahcing attribute). This means that caching settings will cascade down to all the actions in your entire application (in the first case) or in the controller (in the second case).
132+
133+
You can still instruct a specific action to opt out from caching by using `[IgnoreCacheOutput]` attribute.
134+
135+
[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
136+
public class IgnoreController : ApiController
137+
{
138+
[Route("cached")]
139+
public string GetCached()
140+
{
141+
return DateTime.Now.ToString();
142+
}
143+
144+
[IgnoreCacheOutput]
145+
[Route("uncached")]
146+
public string GetUnCached()
147+
{
148+
return DateTime.Now.ToString();
149+
}
150+
}
151+
129152
Server side caching
130153
--------------------
131154
By default **CacheOutput** will use *System.Runtime.Caching.MemoryCache* to cache on the server side. However, you are free to swap this with anything else
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Web.Http;
3+
4+
namespace WebApi.OutputCache.V2.Demo
5+
{
6+
[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
7+
[RoutePrefix("ignore")]
8+
public class IgnoreController : ApiController
9+
{
10+
[Route("cached")]
11+
public string GetCached()
12+
{
13+
return DateTime.Now.ToString();
14+
}
15+
16+
[IgnoreCacheOutput]
17+
[Route("uncached")]
18+
public string GetUnCached()
19+
{
20+
return DateTime.Now.ToString();
21+
}
22+
}
23+
}

sample/WebApi.OutputCache.V2.Demo/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Program
1010
static void Main(string[] args)
1111
{
1212
var config = new HttpSelfHostConfiguration("http://localhost:999");
13+
config.MapHttpAttributeRoutes();
1314
config.Routes.MapHttpRoute(
1415
name: "DefaultApi",
1516
routeTemplate: "api/{controller}/{id}",

sample/WebApi.OutputCache.V2.Demo/TeamsController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public IEnumerable<Team> Get()
2121
return Teams;
2222
}
2323

24-
[CacheOutputUntil(2014, 7, 20)]
24+
[CacheOutputUntil(2016, 7, 20)]
2525
public Team GetById(int id)
2626
{
2727
var team = Teams.FirstOrDefault(i => i.Id == id);

sample/WebApi.OutputCache.V2.Demo/WebApi.OutputCache.V2.Demo.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
<Reference Include="System.Xml" />
6666
</ItemGroup>
6767
<ItemGroup>
68+
<Compile Include="IgnoreController.cs" />
6869
<Compile Include="Teams2Controller.cs" />
6970
<Compile Include="Program.cs" />
7071
<Compile Include="Team.cs" />

src/WebApi.OutputCache.V2/CacheOutputAttribute.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
namespace WebApi.OutputCache.V2
1919
{
20-
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
20+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
2121
public class CacheOutputAttribute : ActionFilterAttribute
2222
{
2323
private const string CurrentRequestMediaType = "CacheOutput:CurrentRequestMediaType";
@@ -73,14 +73,23 @@ protected virtual void EnsureCache(HttpConfiguration config, HttpRequestMessage
7373

7474
internal IModelQuery<DateTime, CacheTime> CacheTimeQuery;
7575

76-
readonly Func<HttpActionContext, bool, bool> _isCachingAllowed = (ac, anonymous) =>
76+
protected virtual bool IsCachingAllowed(HttpActionContext actionContext, bool anonymousOnly)
7777
{
78-
if (anonymous)
78+
if (anonymousOnly)
79+
{
7980
if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
81+
{
8082
return false;
83+
}
84+
}
8185

82-
return ac.Request.Method == HttpMethod.Get;
83-
};
86+
if (actionContext.ActionDescriptor.GetCustomAttributes<IgnoreCacheOutputAttribute>().Any())
87+
{
88+
return false;
89+
}
90+
91+
return actionContext.Request.Method == HttpMethod.Get;
92+
}
8493

8594
protected virtual void EnsureCacheTimeQuery()
8695
{
@@ -133,7 +142,7 @@ public override void OnActionExecuting(HttpActionContext actionContext)
133142
{
134143
if (actionContext == null) throw new ArgumentNullException("actionContext");
135144

136-
if (!_isCachingAllowed(actionContext, AnonymousOnly)) return;
145+
if (!IsCachingAllowed(actionContext, AnonymousOnly)) return;
137146

138147
var config = actionContext.Request.GetConfiguration();
139148

@@ -184,7 +193,7 @@ public override async Task OnActionExecutedAsync(HttpActionExecutedContext actio
184193
{
185194
if (actionExecutedContext.ActionContext.Response == null || !actionExecutedContext.ActionContext.Response.IsSuccessStatusCode) return;
186195

187-
if (!_isCachingAllowed(actionExecutedContext.ActionContext, AnonymousOnly)) return;
196+
if (!IsCachingAllowed(actionExecutedContext.ActionContext, AnonymousOnly)) return;
188197

189198
var cacheTime = CacheTimeQuery.Execute(DateTime.Now);
190199
if (cacheTime.AbsoluteExpiration > DateTime.Now)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System;
2+
3+
namespace WebApi.OutputCache.V2
4+
{
5+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
6+
public sealed class IgnoreCacheOutputAttribute : Attribute
7+
{
8+
}
9+
}

src/WebApi.OutputCache.V2/WebApi.OutputCache.V2.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
<Compile Include="DefaultCacheKeyGenerator.cs" />
6262
<Compile Include="HttpConfigurationExtensions.cs" />
6363
<Compile Include="ICacheKeyGenerator.cs" />
64+
<Compile Include="IgnoreCacheOutputAttribute.cs" />
6465
<Compile Include="InvalidateCacheOutputAttribute.cs" />
6566
<Compile Include="Properties\AssemblyInfo.cs" />
6667
<Compile Include="CacheOutputAttribute.cs" />

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,27 @@ public void can_handle_media_type_when_cache_has_expired_during_request()
305305
Assert.That(result.IsSuccessStatusCode, Is.True);
306306
}
307307

308+
[Test]
309+
public void will_cache_if_cacheouput_present_on_controller()
310+
{
311+
var client = new HttpClient(_server);
312+
var result = client.GetAsync("http://www.strathweb.com/api/ignore/cached").Result;
313+
314+
_cache.Verify(s => s.Contains(It.Is<string>(x => x == "webapi.outputcache.v2.tests.testcontrollers.ignorecontroller-cached:application/json; charset=utf-8")), Times.Exactly(2));
315+
_cache.Verify(s => s.Add(It.Is<string>(x => x == "webapi.outputcache.v2.tests.testcontrollers.ignorecontroller-cached"), It.IsAny<object>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), null), Times.Once());
316+
_cache.Verify(s => s.Add(It.Is<string>(x => x == "webapi.outputcache.v2.tests.testcontrollers.ignorecontroller-cached:application/json; charset=utf-8"), It.IsAny<object>(), It.Is<DateTimeOffset>(x => x <= DateTime.Now.AddSeconds(100)), It.Is<string>(x => x == "webapi.outputcache.v2.tests.testcontrollers.ignorecontroller-cached")), Times.Once());
317+
}
318+
319+
[Test]
320+
public void will_not_cache_if_cacheouput_present_on_controller_but_action_has_ignorecacheouputattibute()
321+
{
322+
var client = new HttpClient(_server);
323+
var result = client.GetAsync("http://www.strathweb.com/api/ignore/uncached").Result;
324+
325+
_cache.Verify(s => s.Contains(It.IsAny<string>()), Times.Never());
326+
_cache.Verify(s => s.Add(It.IsAny<string>(), It.IsAny<object>(), It.IsAny<DateTimeOffset>(), It.IsAny<string>()), Times.Never());
327+
_cache.Verify(s => s.Add(It.IsAny<string>(), It.IsAny<object>(), It.IsAny<DateTimeOffset>(), It.IsAny<string>()), Times.Never());
328+
}
308329

309330
//[Test]
310331
//public void must_add_querystring_to_cache_params()
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Web.Http;
2+
3+
namespace WebApi.OutputCache.V2.Tests.TestControllers
4+
{
5+
[CacheOutput(ClientTimeSpan = 100, ServerTimeSpan = 100)]
6+
public class IgnoreController : ApiController
7+
{
8+
[HttpGet]
9+
public string Cached()
10+
{
11+
return "test";
12+
}
13+
14+
[HttpGet]
15+
[IgnoreCacheOutput]
16+
public string NotCached()
17+
{
18+
return "test";
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)