使用Last-Modified头和OutputCacheAttribute在asp.net MVC

2019-08-17 00:10发布

Edited

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

1) You can use OutputCacheAttribute which works with the help of Expires http header. But it will return 304 Not Modified unless the time expire (even if the image was changed).

2) To avoid displaing stale images You can use Last-Modified http header (with OutputCacheAttribute). In this case the browser sends the request to the server with If-Modified-Since http header. On the server You verify whether the object is still valid or not and if it is You just return Last-Modified http header (and the browser takes image from the local cache); if the object was modified You return it with 200 OK status.
So, the browser needs to send the request to the server each time before taking image from it's own cache. Here is the example -

3) There is another way (as I was told the right way in my case, cause the images will change very rarely... anyway, I need to implement exactly this): To add modified date to the image url and set caching with Expires for the eternity (1 year or more). If the image have changed You should send new url with 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 the LastModifiedCacheAttribute in Global.asax and applied the following OutputCacheAttribute 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 seems like the browser doesn't send requests to the server, instead it just take images from the cache unless the duration is not ended. (When I change the image the browser doesn't display new version)

Questions:

1) How to implement the third approach, so that the browser will take the images from client cache (and will not send the response to the server each time it wants the image) unless the image was modified?
edited: the actual code will be appreciated.

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

Answer 1:

你可以考虑使用的ETag( http://en.wikipedia.org/wiki/HTTP_ETag ),这就是我想读你的问题的第一件事。

你也可以看看这里: 设置ETag的用于FileResult - MVC 3



Answer 2:

如果我理解正确的,你是什么之后,是有无限的缓存,并依靠通过改变资源的实际URL缓存失效。

在这种情况下,我相信实际执行更简单,不需要头的人工处理。

最终,点是能够以具有诸如下面由URL加载的图片:

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

“1”是产品 ID,并指定某种版本标识符(“V”)的。

关键是要建立一个网址,其中v的值取决于图像的最后修改(你或许应该随着图像,或存放)。 你或许可以哈希修改日期,并使用它。

如果下次你建立的URL,最后修改的日期是一样的,你会得到相同的哈希值,因此,作为前呈现相同的URL,浏览器将加载从缓存,而不从服务器请求什么。

一旦图像被更新,并修改日期的改变,你的代码会产生不同的URL,这将迫使浏览器再次请求了。

然后你只需应用OutputCache属性的动作,用于缓存客户端(无需指定上VaryByParam ),你应该设置。



Answer 3:

您可以使用VaryByCustom是选项和输出缓存来实现这一不使用自定义属性。 更改你这样的方法的代码:

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

然后将下面的代码添加到您的Global.asax:

    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);
        }
    }

当图像文件更改的时间戳,该GetVaryByCustomString方法的返回值会发生变化,这将导致ASP.NET重新加载图像,而不是使用缓存的值。

见http://msdn.microsoft.com/en-us/library/aa478965.aspx进一步的细节。



Answer 4:

Intially使用这个答案,但对于当图像被修改,它不会在客户端更新一些原因,而不是显示图像的缓存版本。

因此, 解决办法是使用的版本哪个是你的3'rd选项,只是从你的产品形象数据库添加LASTUPDATED日期时间字段

操作方法

    [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");
    }

在查看

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

取自理念这个职位。

答案是迟,但希望可以帮助别人。



文章来源: Client side caching using Last-Modified header and OutputCacheAttribute in asp.net mvc 3