使用asp.net mvc 3中的Last-Modified标头和OutputCacheAttribute进行客户端缓存

时间:2013-02-16 19:27:26

标签: asp.net asp.net-mvc asp.net-mvc-3 caching browser-cache

被修改

我想在客户端上缓存图像并知道在mvc 3中有不同的方法:(如果我错了,请纠正我)

1)您可以使用OutputCacheAttribute,它可以在Expires http标头的帮助下工作。但它将返回304 Not Modified,除非时间到期(即使图像已更改)。

2)避免替换陈旧图像您可以使用Last-Modified http标头(使用OutputCacheAttribute)。在这种情况下,浏览器使用If-Modified-Since http标头将请求发送到服务器。在服务器上验证对象是否仍然有效,如果是,您只需返回Last-Modified http标头(浏览器从本地缓存中获取图像);如果对象已被修改,则返回200 OK状态 因此,浏览器需要每次在从其自己的缓存中获取图像之前将请求发送到服务器。 Here is the example -

3)还有另一种方式(因为在我的情况下,我被告知正确的方式,因为图像将很少改变...无论如何,我需要实现这一点):要将修改日期添加到图像网址和永久设置缓存Expires(1年或更长时间)。如果图像已更改您应该发送新版本的新网址。

以下是代码:

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

我在Global.asax中注册了LastModifiedCacheAttribute,并将以下OutputCacheAttribute应用于我的操作方法。

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

如果我使用上面的代码似乎浏览器没有向服务器发送请求,而只是从缓存中获取图像,除非持续时间没有结束。 (当我更改图像时,浏览器不显示新版本)

问题:

1)如何实现第三种方法,以便浏览器从客户端缓存中获取图像(并且每次想要图像时都不会将响应发送到服务器),除非图像被修改了?
已修改:我们将非常感谢您的实际代码。

2)在上面的代码中,第一个图像请求的时间被写入Last-Modified(不知道为什么)。如何将文件的修改日期写入Last-Modified?
已编辑:此问题与第二种方法有关。此外,如果我仅在客户端上缓存并使用Last-Modified实施,则仅当我按304 Not Modified时才会获得F5状态。如果我重新输入相同的网址,我会得到200 OK。如果我在不使用Last-Modified的情况下缓存客户端,无论如何都会返回200 OK。怎么能解释这个?

4 个答案:

答案 0 :(得分:1)

您可以考虑使用ETags(http://en.wikipedia.org/wiki/HTTP_ETag),这是我第一次想到阅读您的问题。

您还可以在这里查看:Set ETag for FileResult - MVC 3

答案 1 :(得分:0)

如果我理解正确,你所追求的是拥有无限缓存,并依赖于通过更改资源的实际网址来使缓存失效。

在这种情况下,我相信实际的实现要简单得多,并且不需要手动处理标题。

最终,重点是能够通过URL加载图像,如下所示:

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

“1”是 productId ,并指定某种版本标识符(“v”)。

关键是构建该URL,其中 v 的值取决于图像的最后修改(您应该将其与图像或产品一起存储)。您可以散列修改日期,然后使用它。

如果下次构建url时,上次修改的日期相同,您将获得相同的哈希值,因此呈现与以前相同的URL,并且浏览器将从缓存中加载该URL而不请求任何内容来自服务器。

一旦图像更新,并且修改日期发生变化,您的代码将生成不同的网址,这将迫使浏览器再次请求它。

然后,您只需将OutputCache属性应用于Action,配置为在客户端缓存(无需指定VaryByParam),您应该设置。

答案 2 :(得分:0)

您可以将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

答案 3 :(得分:0)

最初使用的this答案,但由于某种原因,当图像被修改时,它不会在客户端更新,而是显示图像的缓存版本。

所以解决方案正在使用版本控制这是您的第3选项,只需从产品图片数据库添加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 })" />

来自this帖子的想法。

答案很晚,但希望帮助了某人。