Giter Club home page Giter Club logo

strathweb.cacheoutput's Introduction

๐Ÿ“ข This repo is no longer maintained ๐Ÿ“ข

First of all, I apologize it took so long to make an announcement, but I think it has been clear to everyone from the update frequency that this repo is no longer maintained.

I would therefore like to officially (re)state that, indeed, this repo and the corresponding library is no longer maintained. I will archive it soon.

It served its purpose back in the ASP.NET Web API days, and I hope it helped some people build their products. The ASP.NET landscape is obviously much different now, and, given many other involvements, I do not have the capacity or energy to keep this legacy thing alive. I am sorry if anyone feels let down by this. If you are using it you and still need it, you are of course free to fork and make any changes you wish.

I would like to thank almost 1k people who starred this repo and everyone who contributed to over 3 million downloads on Nuget. Thank you all.

ASP.NET Web API CacheOutput

A small library bringing caching options, similar to MVC's "OutputCacheAttribute", to Web API actions.

CacheOutput will take care of server side caching and set the appropriate client side (response) headers for you.

You can specify the following properties:

  • ClientTimeSpan (corresponds to CacheControl MaxAge HTTP header)
  • MustRevalidate (corresponds to MustRevalidate HTTP header - indicates whether the origin server requires revalidation of a cache entry on any subsequent use when the cache entry becomes stale)
  • ExcludeQueryStringFromCacheKey (do not vary cache by querystring values)
  • ServerTimeSpan (time how long the response should be cached on the server side)
  • AnonymousOnly (cache enabled only for requests when Thread.CurrentPrincipal is not set)

Additionally, the library is setting ETags for you, and keeping them unchanged for the duration of the caching period. Caching by default can only be applied to GET actions.

Installation

You can build from the source here, or you can install the Nuget version:

For Web API 2 (.NET 4.5)

PM> Install-Package Strathweb.CacheOutput.WebApi2

For Web API 1 (.NET 4.0)

PM> Install-Package Strathweb.CacheOutput

Usage

//Cache for 100 seconds on the server, inform the client that response is valid for 100 seconds
[CacheOutput(ClientTimeSpan = 100, ServerTimeSpan = 100)]
public IEnumerable<string> Get()
{
    return new string[] { "value1", "value2" };
}

//Cache for 100 seconds on the server, inform the client that response is valid for 100 seconds. Cache for anonymous users only.
[CacheOutput(ClientTimeSpan = 100, ServerTimeSpan = 100, AnonymousOnly = true)]
public IEnumerable<string> Get()
{
    return new string[] { "value1", "value2" };
}

//Inform the client that response is valid for 50 seconds. Force client to revalidate.
[CacheOutput(ClientTimeSpan = 50, MustRevalidate = true)]
public string Get(int id)
{
    return "value";
}

//Cache for 50 seconds on the server. Ignore querystring parameters when serving cached content.
[CacheOutput(ServerTimeSpan = 50, ExcludeQueryStringFromCacheKey = true)]
public string Get(int id)
{
    return "value";
}

Variations

CacheOutputUntil is used to cache data until a specific moment in time. This applies to both client and server.

//Cache until 01/25/2013 17:00
[CacheOutputUntil(2013,01,25,17,00)]
public string Get_until25012013_1700()
{
    return "test";
}

CacheOutputUntilToday is used to cache data until a specific hour later on the same day. This applies to both client and server.

//Cache until 23:55:00 today
[CacheOutputUntilToday(23,55)]
public string Get_until2355_today()
{
    return "value";
}

CacheOutputUntilThisMonth is used to cache data until a specific point later this month. This applies to both client and server.

//Cache until the 31st day of the current month
[CacheOutputUntilThisMonth(31)]
public string Get_until31_thismonth()
{
    return "value";
}

CacheOutputUntilThisYear is used to cache data until a specific point later this year. This applies to both client and server.

//Cache until the 31st of July this year
[CacheOutputUntilThisYear(7,31)]
public string Get_until731_thisyear()
{
    return "value";
}

Each of these can obviously be combined with the 5 general properties mentioned in the beginning.

Caching convention

In order to determine the expected content type of the response, CacheOutput will run Web APIs internal content negotiation process, based on the incoming request & the return type of the action on which caching is applied.

Each individual content type response is cached separately (so out of the box, you can expect the action to be cached as JSON and XML, if you introduce more formatters, those will be cached as well).

Important: We use action name as part of the key. Therefore it is necessary that action names are unique inside the controller - that's the only way we can provide consistency.

So you either should use unique method names inside a single controller, or (if you really want to keep them the same names when overloading) you need to use ActionName attribute to provide uniqeness for caching. Example:

[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
public IEnumerable<Team> Get()
{
    return Teams;
}

[ActionName("GetById")]
[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
public IEnumerable<Team> Get(int id)
{
    return Teams;
}

If you want to bypass the content negotiation process, you can do so by using the MediaType property:

[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50, MediaType = "image/jpeg")]
public HttpResponseMessage Get(int id)
{
    var response = new HttpResponseMessage(HttpStatusCode.OK);
    response.Content = GetImage(id); // e.g. StreamContent, ByteArrayContent,...
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
    return response;
}

This will always return a response with image/jpeg as value for the Content-Type header.

Ignoring caching

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).

You can still instruct a specific action to opt out from caching by using [IgnoreCacheOutput] attribute.

[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
public class IgnoreController : ApiController
{
    [Route("cached")]
    public string GetCached()
    {
	return DateTime.Now.ToString();
    }

    [IgnoreCacheOutput]
    [Route("uncached")]
    public string GetUnCached()
    {
	return DateTime.Now.ToString();
    }
}

Server side caching

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 (static Dictionary, Memcached, Redis, whatever..) as long as you implement the following IApiOutputCache interface (part of the distributed assembly).

public interface IApiOutputCache
{
	T Get<T>(string key) where T : class;
	object Get(string key);
	void Remove(string key);
	void RemoveStartsWith(string key);
	bool Contains(string key);
	void Add(string key, object o, DateTimeOffset expiration, string dependsOnKey = null);
}

Suppose you have a custom implementation:

public class MyCache : IApiOutputCache
{
	// omitted for brevity
}

You can register your implementation using a handy GlobalConfiguration extension method:

//instance
configuration.CacheOutputConfiguration().RegisterCacheOutputProvider(() => new MyCache());

// singleton
var cache = new MyCache();
configuration.CacheOutputConfiguration().RegisterCacheOutputProvider(() => cache);

If you prefer CacheOutput to use resolve the cache implementation directly from your dependency injection provider, that's also possible. Simply register your IApiOutputCache implementation in your Web API DI and that's it. Whenever CacheOutput does not find an implementation in the GlobalConiguration, it will fall back to the DI resolver. Example (using Autofac for Web API):

cache = new MyCache();
var builder = new ContainerBuilder();
builder.RegisterInstance(cache);
config.DependencyResolver = new AutofacWebApiDependencyResolver(builder.Build());

If no implementation is available in neither GlobalConfiguration or DependencyResolver, we will default to System.Runtime.Caching.MemoryCache.

Each method can be cached multiple times separately - based on the representation (JSON, XML and so on). Therefore, CacheOutput will pass dependsOnKey value (which happens to be a prefix of all variations of a given cached method) when adding items to cache - this gives us flexibility to easily remove all variations of the cached method when we want to clear the cache. When cache gets invalidated, we will call RemoveStartsWith and just pass that key.

The default cache store, System.Runtime.Caching.MemoryCache supports dependencies between cache items, so it's enough to just remove the main one, and all sub-dependencies get flushed. However, if you change the defalt implementation, and your underlying store doesn't - it's not a problem. When we invalidate cache (and need to cascade through all dependencies), we call RemoveStartsWith - so your custom store will just have to iterate through the entire store in the implementation of this method and remove all items with the prefix passed.

Cache invalidation

There are three ways to invalidate cache:

  • [AutoInvalidateCacheOutput] - on the controller level (through an attribute)
  • [InvalidateCacheOutput("ActionName")] - on the action level (through an attribute)
  • Manually - inside the action body

Example:

[AutoInvalidateCacheOutput]
public class Teams2Controller : ApiController
{
	[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
	public IEnumerable<Team> Get()
	{
	    return Teams;
	}

	public void Post(Team value)
	{
	    //do stuff
	}
}

Decorating the controller with [AutoInvalidateCacheOutput] will automatically flush all cached GET data from this controller after a successfull POST/PUT/DELETE request.

You can also use the [AutoInvalidateCacheOutput(TryMatchType = true)] variation. This will only invalidate such GET requests that return the same Type or IEnumerable of Type as the action peformed takes as input parameter.

For example:

[AutoInvalidateCacheOutput(TryMatchType = true)]
public class TeamsController : ApiController
{
	[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
	public IEnumerable<Team> Get()
	{
	    return Teams;
	}

	[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
	public IEnumerable<Player> GetTeamPlayers(int id)
	{
	    //return something
	}

	public void Post(Team value)
	{
	    //this will only invalidate Get, not GetTeamPlayers since TryMatchType is enabled
	}
}

Invalidation on action level is similar - done through attributes. For example:

public class TeamsController : ApiController
{
	[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
	public IEnumerable<Team> Get()
	{
	    return Teams;
	}

	[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
	public IEnumerable<Player> GetTeamPlayers(int id)
	{
	    //return something
	}

	[InvalidateCacheOutput("Get")]
	public void Post(Team value)
	{
	    //this invalidates Get action cache
	}
}

Obviously, multiple attributes are supported. You can also invalidate methods from separate controller:

[InvalidateCacheOutput("Get", typeof(OtherController))] //this will invalidate Get in a different controller
[InvalidateCacheOutput("Get")] //this will invalidate Get in this controller
public void Post(Team value)
{
    //do stuff
}

Finally, you can also invalidate manually. For example:

public void Put(int id, Team value)
{
    // do stuff, update resource etc.

    // now get cache instance
    var cache = Configuration.CacheOutputConfiguration().GetCacheOutputProvider(Request);

    // and invalidate cache for method "Get" of "TeamsController"
    cache.RemoveStartsWith(Configuration.CacheOutputConfiguration().MakeBaseCachekey((TeamsController t) => t.Get()));
}

As you see, you can we use expression try to allow you to point to the method in a strongly typed way (we can't unfortunately do that in the attributes, since C# doesn't support lambdas/expression trees in attributes).

If your method takes in arguments, you can pass whatever - we only use the expression tree to get the name of the controller and the name of the action - and we invalidate all variations.

You can also point to the method in a traditional way:

cache.RemoveStartsWith(Configuration.CacheOutputConfiguration().MakeBaseCachekey("TeamsController", "Get"));

Customizing the cache keys

You can provide your own cache key generator. To do this, you need to implement the ICacheKeyGenerator interface. The default implementation should suffice in most situations.

When implementing, it is easiest to inherit your custom generator from the DefaultCacheKeyGenerator class.

To set your custom implementation as the default, you can do one of these things:

// Method A: register directly
Configuration.CacheOutputConfiguration().RegisterDefaultCacheKeyGeneratorProvider(() => new CustomCacheKeyGenerator());

// Method B: register for DI (AutoFac example, the key is to register it as the default ICacheKeyGenerator)
builder.RegisterInstance(new CustomCacheKeyGenerator()).As<ICacheKeyGenerator>(); // this will be default
builder.RegisterType<SuperNiceCacheKeyGenerator>(); // this will be available, and constructed using dependency injection

You can set a specific cache key generator for an action, using the CacheKeyGenerator property:

[CacheOutput(CacheKeyGenerator=typeof(SuperNiceCacheKeyGenerator))]

PS! If you need dependency injection in your custom cache key generator, register it with your DI as itself.

This works for unregistered generators if they have a parameterless constructor, or with dependency injection if they are registered with your DI.

Finding a matching cache key generator is done in this order:

  1. Internal registration using RegisterCacheKeyGeneratorProvider or RegisterDefaultCacheKeyGeneratorProvider.
  2. Dependency injection.
  3. Parameterless constructor of unregistered classes.
  4. DefaultCacheKeyGenerator

JSONP

We automatically exclude callback parameter from cache key to allow for smooth JSONP support.

So:

/api/something?abc=1&callback=jQuery1213

is cached as:

/api/something?abc=1

Position of the callback parameter does not matter.

Etags

For client side caching, in addition to MaxAge, we will issue Etags. You can use the Etag value to make a request with If-None-Match header. If the resource is still valid, server will then response with a 304 status code.

For example:

GET /api/myresource
Accept: application/json

Status Code: 200
Cache-Control: max-age=100
Content-Length: 24
Content-Type: application/json; charset=utf-8
Date: Fri, 25 Jan 2013 03:37:11 GMT
ETag: "5c479911-97b9-4b78-ae3e-d09db420d5ba"
Server: Microsoft-HTTPAPI/2.0

On the next request:

GET /api/myresource
Accept: application/json
If-None-Match: "5c479911-97b9-4b78-ae3e-d09db420d5ba"

Status Code: 304
Cache-Control: max-age=100
Content-Length: 0
Date: Fri, 25 Jan 2013 03:37:13 GMT
Server: Microsoft-HTTPAPI/2.0

License

Licensed under Apache v2. License included.

strathweb.cacheoutput's People

Contributors

abatishchev avatar alexwiese avatar bobend avatar diegoampessan avatar donaldgray avatar drewfreyling avatar ehabelgindy avatar filipw avatar flcdrg avatar jpatapoff avatar korijn avatar lazytom avatar lukasz-pyrzyk avatar mahdi avatar osa avatar peteraritchie avatar petermorlion avatar rjvdboon avatar svenclaesson avatar symbianx avatar thorium avatar vdex42 avatar vegardlarsen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

strathweb.cacheoutput's Issues

Queries can not be applied to a response content of type 'System.Net.Http.ByteArrayContent'.

Hi guys,

Was wondering if anyone has tried to use OData (IQueryable) with OutputCaching.

For some of my methods I get following error (only second call). Also attaching my controller method that causes this issue. What I find very interesting is the fact that a different method had same issue and I have fixed it by changing order of attributes. It doesn't seem to be working for this one though. Any Ideas?

///

/// Gets the matches by round id.
/// Cached for 5 minutes. Maximum of 100 matches per request.
///

/// The round identifier.
///
[Queryable(PageSize = 100)]
[CacheOutput(ClientTimeSpan = 300, ServerTimeSpan = 300, MustRevalidate = true)]
public IQueryable GetMatchesByRound(int roundId)
{
return _repository.GetMatchesByRound(roundId).AsQueryable();
}

{
"Message": "An error has occurred.",
"ExceptionMessage": "Queries can not be applied to a response content of type 'System.Net.Http.ByteArrayContent'. The response content must be an ObjectContent.\r\nParameter name: actionExecutedContext",
"ExceptionType": "System.ArgumentException",
"StackTrace": " at System.Web.Http.QueryableAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)\r\n at System.Web.Http.Filters.ActionFilterAttribute.OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.ActionFilterAttribute.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()\r\n at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()\r\n at System.Web.Http.Controllers.AuthenticationFilterResult.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__0.MoveNext()"
}

Odata URL monitoring using powershell

We are using Odata to expose our data and we have many urls . I need to write a tool so that when it goes down , we are informed about that . Any suggestion on this .

Thanks
himanshu

Cache per mediatype?

Hi,

I'm trying to cache the output of a Web Api call with OutputCache v1. It supports two mediatypes: application/json and text/html. In the first call it caches json, in the second call I cannot retrieve the html version (no matter what's in the accept header), only json returns. How can I cache per mediatype?

Thanks,

bert

AggregateException with jsonp request

Hi,

Thanks for making your code available. I've successfully used it in a couple of WebAPI projects.

However, I've just tried making a jsonp request (via jQuery) to my rest endpoint and am getting the following error on line 97 - "Cannot access a closed Stream". Looks like it's from the ReadAsStringAsync() method.

I don't get any errors when making the request from Fiddler or my C# client, only when jsonp is involved.

For now I've had to disable outputcache for the controller action in question.

Thanks,
Chris

IIS appdomain is crashing

Hi,

Thank you for making caching so easy in Web API.
I just have this problem that sometimes it appears as the caching actually crashes the appdomain in the webserver.

I would really appreciate some help on this :)

I have defined the caching on my controller as:

[AutoInvalidateCacheOutput]
public class MonitorInfoController : ApiController
{
    [Queryable]
    [CacheOutput(ServerTimeSpan = 10, ClientTimeSpan = 10 )]
    public IQueryable<MonitorInfo> Get()
    {
    }
}

This is what I can see in the eventlog of the server:

Exception: System.AggregateException

Message: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.
StackTrace:    at System.Threading.Tasks.TaskExceptionHolder.Finalize()

InnerException: System.ArgumentOutOfRangeException

Message: The UTC time represented when the offset is applied must be between year 0 and 10,000.
Parameter name: offset

StackTrace:    at System.DateTimeOffset..ctor(DateTime dateTime)
   at System.Runtime.Caching.MemoryCacheEntryChangeMonitor.InitDisposableMembers(MemoryCache cache)
   at System.Runtime.Caching.MemoryCache.CreateCacheEntryChangeMonitor(IEnumerable`1 keys, String regionName)
   at WebAPI.OutputCache.Cache.MemoryCacheDefault.Add(String key, Object o, DateTimeOffset expiration, String dependsOnKey)
   at WebAPI.OutputCache.CacheOutputAttribute.<>c__DisplayClass8.<OnActionExecuted>b__4(Task`1 t)
   at System.Threading.Tasks.Task.

Output caching of data from stream

Hello guys,

I've been using the Asp.Net OutputCache for caching Xml/Json return media type calls quite successfully. Great library!

I was also trying to cache web api calls that return a HttpResponseMessage for image or file contents. This is done by setting the HttpResponseMessage.Content property with a stream object.
The web api call works great without OutputCache enabled, but as soon as I enable it, sooner or later the server gets stuck and the client waits for a while to receive a response, until it timeouts. While debugging on the server side, no error is thrown.

Does anyone know if this scenario should be working?

Thank you very much, Miguel

New Feature - CacheProfile + Web.config

with the normal OutputCacheAttribute we can specify a key in web.config using CacheProfile property. This allows the attribute to fetch settings dynamically rather than having to rebuild the project.

It would be great if we could share the same ConfigurationSection with mvc and web api. (would help during transitioning and keep it DRY)

Here is an example of a web.config

  <system.web>
    <caching>
      <outputCacheSettings>
        <outputCacheProfiles>
          <!--
        1 hr cache,
        client browser only, 
        vary by query params  -->
          <add name="DynamicUserHtmlFragment" duration="3600" location="Client" noStore="false" varyByParam="*" />
          <add name="api.comment.count" duration="600" location="Client" noStore="false" varyByParam="*" />
        </outputCacheProfiles>
      </outputCacheSettings>
    </caching>

Here is an example of usage:

    [OutputCache(CacheProfile = "DynamicUserHtmlFragment")]
    public ActionResult ExtDropDownListCategoryUrl(int locationId)
    {
        SelectList list = _xxxRepository.GetCategories(locationId);
        return Json(list, JsonRequestBehavior.AllowGet);
    }

Encoding for cached response problem

Hi, cached version of web api control response has not encoding set properly, how it can be fixed (in screenshot there is example of response, there the content-type of doesnt has the encoding attribute at all).

Thanks.

response-cached

Enable client-side caching only?

I'm implementing a Web API to be consumed by an AngularJS web app & would like to have control over caching for some of the requests. Is the following sufficient to enable just client-side caching?

/// Setting 'ServerTimeSpan = 0' to disable any server-side caching
[CacheOutput(ClientTimeSpan = 100, ServerTimeSpan = 0)]
public IEnumerable<string> Get()
{
    return new string[] { "value1", "value2" };
}

Will this work with OData

Hi,

Thanks for making this code available.

I am testing it on a method that has OData support through the use of the [Queryable] attribute. Using your [CacheOutput] attribute causes the $filter to be ignored.

I realize there are many different versions of the OData packages and I may very well have an old one. I was just curious if you have gotten this to work with OData methods or if it is even possible?

Thanks for any input you can provide.

InvalidateCacheOutput(String, Type) attribute doesn't work

The Controller name has "Controller" removed in the attribute constructor, however the CacheOutputAttributes writes to the cache with the Controller still in the name. This means that when it looks for the cache key to invalidate (i.e. WebApiCache.Contains(key)) it doesn't exist.

For example, the key that is created to cache my output is:
"readpositioncontroller-readposition-positionID=2:application/json"

When I try to invalidate the cache using the attribute [InvalidateCacheOutput("ReadPosition", typeof(ReadPositionController))] the key it attempts to find is:
"readposition-readposition", which doesn't exist.

It looks for this because the constructor for the attribute assigns type.Name.Replace("Controller", string.Empty) to controller instead of just using the type.Name directly. If I remove the .Replace("Controller", string.Empty) it works as expected.

eTag changes when content does not

This could be a choice on the implementation of eTags. If the response in the cache expires and a new version of the response is inserted the eTag changes even though the value if the HttpResponse.Content is exactly the same.

Suggestion to put in an option in the Attribute to keep the same eTag if the content is the same.

Current Implementation:
[CacheOutput(ClientTimeSpan = 600, ServerTimeSpan = 600)]

Suggested:
[CacheOutput(ClientTimeSpan = 600, ServerTimeSpan = 600, PreserveETag = true)]

Feature Request - web.config driven cache settings

Allow the ability to configure various cache settings, specifically ClientTimeSpan, ServerTimeSpan from web.config. Maybe through the already existing OutputCacheSettings schema? Thoughts? Willing to implement

Nuget Install-Package fails when installing into a MVC 5 project

Install failed. Rolling back...
Install-Package : Updating 'Microsoft.AspNet.WebApi.Core 5.0.0' to 'Microsoft.AspNet.WebApi.Core 4.0.30506.0' failed. Unable to find versions of 'Microsoft.AspNet.WebApi.WebHost,
Microsoft.AspNet.WebApi.OData' that are compatible with 'Microsoft.AspNet.WebApi.Core 4.0.30506.0'.
At line:1 char:1

  • Install-Package Strathweb.CacheOutput
  • - CategoryInfo          : NotSpecified: (:) [Install-Package], InvalidOperationException
    - FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PowerShell.Commands.InstallPackageCommand
    

Cannot Invalidate Cache

I have readonly API controller that only will be changed based on PUT/POST on another controller.

I tried using InvalidateCacheOutput("Get", typeof(OtherController))] filter and manually cache.RemoveStartsWith() as suggested by the readme file to invalidate the cached output for the other controller. However it does not really invalidate since I can see 304 Not Modified by using Fiddler2. It should 200 Ok on first request after the invalidate.

It quite frustrating me after try an error in a few hours, did I miss something?

Set cache durations from Action code, not attribute

I'd like to be able to modify the cache durations based on some aspect of the data being returned (e.g. data in the past is very cacheable, data in the present isn't, and data in the future can be cached for short durations).

Any possibilty?

How to invalidate cache?

What is the recommended way of usage for infrequent changed data (for example 1x per month)?

Sample:

[CacheOutput(???)]
public string GetUsers(int id)
{
...
}

public string AddUser(user u)
{
//HOWTO: clear cache of GetUsers method
...
}

jsonp

This is very helpful.

I do have a question/issue to mention though, How do you cache/handle jsonp calls?
Since for jsonp calls the query string tends to be similar to
http://localhost:60074/api/order?data=022eb4b7dd5e33ddbfcc1dab63bd203f&callback=jQuery18306690503899008_879798

The 'callback' variable's value will be different for each call, though the input variable 'data' may be contain same value for various calls. Is there a way to ignore 'callback' variable's value while caching?

Doesn't play nicely with MessageHandlers

Currently have I have a MessageHandler that decorates my models with hypermedia links. When I add the server caching options (client only is fine) then the MessageHandler runs, but the cache serves the version from before it has run (so no links).

Caching by parameters not working

In AspNetWebApi-OutputCache / src / WebAPI.OutputCache / CacheOutputAttribute.cs, it looks like the call to WebApiCache.RemoveStartsWith(baseKey); removes all other keys for a specific controller-action. This means you only get 1 cache'd item per method instead of multiple items per method based on the method + method parameters.

Web API V2 NO GO

Unfortunately I get errors when installing into a web api v2 project. Is there a fix for this in the works?

Usage of anonmyousOnly doesn't make much sense for WebApi

The use of anonymousOnly does not make much sense for APIs since we aren't dealing with static vs. dynamic page content. I think the behavior should change to allowAnonymous - cache for all users vs. authenticated users only. Thoughts?

Using custom header value as part of cache-key?

This is a question:
Is it currently possible, or is it easy to add the functionality such that if I have a query like:

/api/vehicle?code=ABCD

that it is cached seperately depending upon a custom header value -- what I'm trying to achieve here, and maybe there are more elegant solutions is; I have a custom header that indicates a customer/client that is making the request .. for argument sake lets call it an API key:

X-Api-Key: 11111-11111-1111-1111 etc

Now for the same given query string, I would like the cache seperated by the X-Api-Key value, so that a client coming in with X-Api-Key of 22222-2222-222-222 etc with the same query string of /api/vehicle?code=ABCD won't see or use previously cached value.

The reason behind this is that I want to try to use caching to minimise the number of 'requeries' but I need to account for each individual query made by different clients .. so within a caching period (24hrs) I want to see each unique request from each customer coming through .. so that I can account for that.

Hope this makes sense.

Regards
Brian Dukes

Should results be cached when the request isn't succesful

I wonder whether the result from an Action should be cached when the Action was not succesful. My first impression tells me it shouldn't cache, or that you should be able to control the behaviour through a parameter on the CacheOutput attibute.

Debug mode unexpected 'step-in'

Hi,

Thanks for this code. Very cool!

I have the package installed, referenced and working.

The client caching works as expected but when I apply a [ServerTimeSpan] tag to a method and run in debug I would expect to only step into that controller/action method on the first request and not again until the ServerTimeSpan has elapsed.
What happens is that each refresh of the browser causes the controller/action method to rerun, what am I doing wrong?

Thanks!

Caching

Hi Filip,

Thanks for the support and help to implement the Caching in Web API. As your NuGet package is Third party implementation like to know if Microsoft has released any enhancement or NuGet package to implement the caching in Web API 1.0.

Regards
Raju
http://weblogs.asp.net/sukumarraju/default.aspx

Getting Transaction Scope errors

My application is using a .NET's TransactionScope. There will be only one transaction scope per request.When we deployed the code to my staging machine with WebApi-OutputCache we are getting 'The current transaction is completed'.When I removed it we are not getting any errors.We are using IoC container 'Spring'.

Client Side caching does not work for me.

Client Side caching does not work well in my case.

Attribute I used is

[CacheOutput(ServerTimeSpan=3600, ClientTimeSpan=3600, ExcludeQueryStringFromCacheKey=true)]

as per the document it should cache on client as well. Server caching indeed works great. if you look at attached image it does not fetch data from cache.

cache

Backed by Windows Azure Caching on Roles

Hi,

Thanks for sharing this great library. I'm now deploying to Azure and I want to add caching to my Web Api application. But I also want my Web API app to be deployable over regular IIS deployment so I don't want to be dependant over Azure stuff. So I'll try to develop a IApiOutputCache that is using Azure Caching on Role. Would you accept my contribution if it works ?

Thanks
Jean

First request after cache invalidates doesn't go into cache

A method is decorated like so:
CacheOutput(ServerTimeSpan = 4, ClientTimeSpan = 4)]

and lets say I perform same request every second
1s no cache (first request so it's ok)
2s cache
3s cache
4s cache
5s no cache (ok since cache has timed out)
6s no cache (this should've been cached by request done at 5s)
7s cache

System.OperationCanceledException: The operation was canceled.

I just added this caching framework to my web API methods and getting this error below quite a bit. Any reason why? Didnt happen before.

System.OperationCanceledException: The operation was canceled.
   at System.Threading.CancellationToken.ThrowOperationCanceledException()
   at System.Web.Http.WebHost.HttpControllerHandler.<WriteStreamedResponseContentAsync>d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.WebHost.HttpControllerHandler.<CopyResponseAsync>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.WebHost.HttpControllerHandler.<ProcessRequestAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.TaskAsyncHelper.EndTask(IAsyncResult ar)
   at System.Web.HttpTaskAsyncHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Allow custom implementations of CacheOutput to set times in OnActionExecuted

I am inheriting from CacheOutputAttribute but need to set the client and server cache times from a value calculated within the controller's action method.

I'm doing this by overriding OnActionExecuted. Setting the ClientTimeSpan and ServerTimeSpan is possible in the overridden method, but because the CacheTimeQuery field has already been set in CacheOutputAttribute.OnActionExecuting, the updated values are not used.

eg.

    [AttributeUsage( AttributeTargets.Method, AllowMultiple = false, Inherited = true )]
    public class FeedCacheOutputAttribute : CacheOutputAttribute
    {
        public override void OnActionExecuted( System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext )
        {
            var feed = (Feed) actionExecutedContext.ActionContext.ModelState[ "feed" ].Value.RawValue;

            var cacheTimeSeconds = (int) feed.ValidUntil.Subtract( DateTimeOffset.UtcNow ).TotalSeconds;

            ClientTimeSpan = cacheTimeSeconds;
            ServerTimeSpan = cacheTimeSeconds;

            // Need to be able to set CacheTimeQuery here

            base.OnActionExecuted( actionExecutedContext );
        }
    }

AllKeys functionality on IApiOutputCache

Would be useful to be able to return an array of keys in the cache

Can do it (on MemoryCacheDefault) like this...

return Cache.Select(x=>x.Key);

Just wondered if this was viable?

Problem caching when using callbacks

The unit tests fail that use callbacks. This is because of an error in the code around BaseCacheAttribute.GetValue.

if (val is IEnumerable)
should be:
if (val is IEnumerable && !(val is string))

Reason being is string implements IEnumerable so when you try to match parameter "callback=" + callbackValue in BaseCacheAttribute.MakeCacheKey it will never match as the callback value has been changed to a;b;c; by GetValue.

Additional differentiation to the cache key?

I have a case where a certain URL needs to reply with different content to different users. Not very RESTful, I know.

However, in that case it would be beneficial to be able to override the generation of the cache key for a specific action. The easiest way to do this is to add a new Func<string> CacheKeyDifferentiator (suggestions for a better name is very welcome) property to CacheOutputAttribute and append the generated string to the cache key.

Would a pull request for this functionality be welcome?

ContentType null

Using V2 line 153 of CacheOutputAttribute.cs:

_webApiCache.Add(cachekey + Constants.ContentTypeKey, actionExecutedContext.Response.Content.Headers.ContentType.MediaType, cacheTime.AbsoluteExpiration, baseKey);

Headers.ContentType = null.

I'm decorating my GET function like:

HttpGet
ActionName("GetThis")
CacheOutput(ServerTimeSpan:=5, ClientTimeSpan:=0)
Public Function DoGetThis(ByVal guid As String) As HttpResponseMessage

Returning:
New HttpResponseMessage() With {.Content = New JsonContent(New With {Key .Success = True, Key .Message = "Success", .NowPlaying = data})}

data is my own business logic class.
JsonContent is from http://stackoverflow.com/questions/14231877/how-to-return-json-object-on-web-api-controller

Am I doing something wrong?

Inefficient workflow can cause huge delays in large application

It is not entirely clear to me how to tell the framework to only do server side caching, or only do client side caching. Maybe an Enum propoperty on CacheOutputAttribute configuring that would do the job. If it's meant to be controlled by the Server and Client TimeSpans, then it's not working very well, here is why

In my scenario, I only want the client side caching:

[CacheOutput(ClientTimeSpan = 59)]

however the implementation of CacheOutputAttribute is running a lot of initialization even if the ServerTimeSpan is <= 0.

Specifically these two methods

EnsureCache(config, actionContext.Request);

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

end up calling the DependencyResolver.GetDependencyScope().GetService(), which is unnecessary.

The problem really stands out when using Unity, and the default implementation of Unity.WebApi.UnityDependencyResolver from Unity.WebAPI nuget package, that takes 100-200 mls to try to resolve the dependency, eventually failing anyway.

executing all this code takes hundreds of milliseconds and significantly delays each call. When multiple calls, the delays rise linearly to seconds.

One could avoid all of this by refactiring the code in CacheOutputAttribute.OnActionExecuting(HttpActionContext actionContext) to:

private void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext == null) throw new ArgumentNullException("actionContext");

        if (!_isCachingAllowed(actionContext, AnonymousOnly)) return;

        EnsureCacheTimeQuery();

        if (this.ServerTimeSpan <= 0)
            return;

.....

Invalidate Action with parameters

A have a action decorated like this

[HttpGet, CacheOutput(ClientTimeSpan = CacheSettings.OneHourCache, ServerTimeSpan = CacheSettings.OneHourCache)]
public List GetCategoryItems(string site)

This key is generated for the request
neoassist-getcategoryitems-site=xml:application/json

How can i invalidate this action? i've tried this
var cache = GlobalConfiguration.Configuration.CacheOutputConfiguration().GetCacheOutputProvider(Request);
cache.RemoveStartsWith(Configuration.CacheOutputConfiguration().MakeBaseCachekey((NeoAssistController t) => t.GetCategoryItems("xml")));

But the key returned is => neoassist-getcategoryitems

MakeBaseCachekey

Hi Filip,

I've run into a problem with the MakeBaseCachekey method because the base cache key is not unique for me. I noticed in "Allow using custom cache keys. #40" that you've discussed how this might be overcome. Do you have any plans to change this, or any recommendations on how this might be changed?

The issue I'm encountering is that I have some entities with the same name but they are in different namespaces. The controller name is the same e.g. ReadServiceCategoryController but they exist in different DLLs or in different namespaces within the same DLL. They even have the same method names because they are automatically generated. MakeBaseCachekey creates the same base key for both of the controllers so they return incorrect information depending on which controller is called first.

I've seen that a new key generator can be plugged in but the MakeBaseCachekey method is still used in some places that can't be replaced. I'd like to fix this in a way that you'd be happy to pull back into the main branch so that I don't get stuck on a fork of your project.

Kind regards,
Dave

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.