Client-side caching using Last-Modified header and OutputCacheAttribute in asp.net mvc 3

Edited

I want to cache images on the client and know that there are different ways to do this in mvc 3: (correct me if I am wrong)

1) You can use OutputCacheAttribute , which works using the Expires http header. But it will return 304 Not Modified if the time does not expire (even if the image has been resized).

2) To avoid stale images, you can use the Last-Modified http header (with OutputCacheAttribute ). In this case, the browser sends a request to the server with the If-Modified-Since http header. On the server, you check whether the object remains valid or not, and if you simply return the Last-Modified http-header (and the browser takes an image from the local cache); if the object has been modified, you return it with a status of 200 OK .
Thus, the browser must send a request to the server each time before taking the image from its own cache. Here is an example -

3) There is another way (as I was told correctly in my case, because images will change very rarely ... in any case, I need to implement just that): To add the changed date to the image URL and set the caching with Expires for eternity (1 year or more). If the image has changed, you should send a new url with the new version.

Here is the code:

 public class LastModifiedCacheAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { if (filterContext.Result is FilePathResult) { var result = (FilePathResult)filterContext.Result; var lastModify = File.GetLastWriteTime(result.FileName); if (!HasModification(filterContext.RequestContext, lastModify)) filterContext.Result = NotModified(filterContext.RequestContext, lastModify); SetLastModifiedDate(filterContext.RequestContext, lastModify); } base.OnActionExecuted(filterContext); } private static void SetLastModifiedDate(RequestContext requestContext, DateTime modificationDate) { requestContext.HttpContext.Response.Cache.SetLastModified(modificationDate); } private static bool HasModification(RequestContext context, DateTime modificationDate) { var headerValue = context.HttpContext.Request.Headers["If-Modified-Since"]; if (headerValue == null) return true; var modifiedSince = DateTime.Parse(headerValue).ToLocalTime(); return modifiedSince < modificationDate; } private static ActionResult NotModified(RequestContext response, DateTime lastModificationDate) { response.HttpContext.Response.Cache.SetLastModified(lastModificationDate); return new HttpStatusCodeResult(304, "Page has not been modified"); } } 

And I registered LastModifiedCacheAttribute in Global.asax and applied the following OutputCacheAttribute method to my action method.

 [HttpGet, OutputCache(Duration = 3600, Location = OutputCacheLocation.Client, VaryByParam = "productId")] public FilePathResult GetImage(int productId) { // some code } 

If I use the code above, it looks like the browser is not sending requests to the server, instead, it just takes images from the cache if the duration does not end. (When I change the image, the browser does not display the new version)

Questions:

1) . How to implement the third approach, so that the browser takes images from the client’s cache (and will not send a response to the server every time it wants an image), if only the image was changed?
edited: the actual code will be evaluated.

2) In the above code, the time of the first image request is recorded in Last-Modified (I don’t know why). How to write file modification date in Last-Modified?
edited: this question relates to the second approach. Also, if I only cache on the client and use the Last-Modified implementation, I only get the status 304 Not Modified if I press F5 . If I return to the same URL, I will get 200 OK . If I cache on the client without using Last-Modified , it will always return 200 OK no matter what. How can this be explained?

+4
source share
4 answers

You can learn the ETags ( http://en.wikipedia.org/wiki/HTTP_ETag ), which I thought about reading your question first.

You can also look here: Install ETag for FileResult - MVC 3

+1
source

If I understand correctly, then what you need has infinite caching and relies on cache invalidation by changing the actual resource URL.

In this case, I find that the actual implementation is much simpler and does not require manual processing of the headers.

Ultimately, the challenge is to have images uploaded using a URL, for example:

 http://someDomain/product/image/1?v={something} 

With "1" is the productId and indicating some kind of version identifier ("v").

The key is to build this url, where the value of v depends on the last modification of the image (which you probably should keep with the image or product). You can probably change the date of change and use this.

If the next time you create the URL, the date of the last modification is the same, you will get the same hash value and therefore display the same URL as before, and the browser will load it from the cache without request from the server.

As soon as the image is updated and this change date changes, your code will generate a different URL that will cause the browser to request it again.

Then you simply apply the OutputCache attribute to the Action configured for caching on the client (no need to specify VaryByParam ), and you should be set.

0
source

You can use the VaryByCustom option with output caching to achieve this without using a custom attribute. Change the method code as follows:

 [HttpGet, OutputCache(Duration = 3600, Location = OutputCacheLocation.Client, VaryByCustom = "imagedate")] public FilePathResult GetImage(int productId) { // some code } 

Then add the following code to your Global.asax file:

  public override string GetVaryByCustomString(System.Web.HttpContext context, string custom) { if (custom.ToLower() == "imagedate") { return System.IO.File.GetLastWriteTime(Server.MapPath("~/Images/my-image.png")).ToString(); } else { return base.GetVaryByCustomString(context, custom); } } 

When the timestamp of the image file changes, the return value of the GetVaryByCustomString method changes, which causes ASP.NET to reload the image rather than use the cached value.

See http://msdn.microsoft.com/en-us/library/aa478965.aspx for more details.

0
source

This answer is mainly used, but for some reason, when the image is changed, it is not updated on the client, instead, a cached version of the image is displayed.

So the solution uses the version that is your 3rd option, just add the LastUpdated datetime field from the product image database

Action method

  [HttpGet] [OutputCache( Duration = 7200, VaryByParam = "productId;lastUpdated", Location = OutputCacheLocation.Client)] public ActionResult GetImage(string productId, string lastUpdated) { var dir = Server.MapPath("~/productimages/"); var path = Path.Combine(dir, productId + ".jpg"); return base.File(path, "image/jpeg"); } 

In view

 <img src="@Url.Action("GetImage", "Home", new { productId = "test-product-100", lastUpdated =Model.LastUpdated })" /> 

The idea is taken from this publication.

The answer is delayed, but Hope helps someone.

0
source

All Articles