Last-Modified头在MVC(Last-Modified Header in MVC)

2019-06-24 05:55发布

我最近遇到的Last-Modified头。

  • 如何以及在哪里可以包括在MVC?
  • 什么是包括它的优势是什么?

我想一个例子头怎么最后修改可以包含在一个MVC项目,静态网页和数据库查询呢?

它是从输出缓存不同,如果是如何?

基本上,我希望浏览器清除缓存,并自动显示最新数据或网页,而无需用户做了刷新或清除缓存。

Answer 1:

Last-Modified主要用于缓存。 它发回的资源,你可以跟踪修改时间。 资源并不一定是文件,但任何事情。 对于那些从那里你有一个分贝的信息生成的实例页面UpdatedAt列。

其在与组合使用If-Modified-Since每个浏览器在请求发送(如果其已接收到报头Last-Modified标头之前)。

如何以及在哪里可以包括在MVC?

Response.AddHeader

什么是包括它的优势是什么?

启用这些动态生成的(例如,你可以使用你的数据库字段的网页细粒度缓存UpdatedAt作为最后修改的头)。

为了使一切工作,你必须做这样的事情:

public class YourController : Controller
{
    public ActionResult MyPage(string id)
    {
        var entity = _db.Get(id);
        var headerValue = Request.Headers['If-Modified-Since'];
        if (headerValue != null)
        {
            var modifiedSince = DateTime.Parse(headerValue).ToLocalTime();
            if (modifiedSince >= entity.UpdatedAt)
            {
                return new HttpStatusCodeResult(304, "Page has not been modified");
            }
        }

        // page has been changed.
        // generate a view ...

        // .. and set last modified in the date format specified in the HTTP rfc.
        Response.AddHeader('Last-Modified', entity.UpdatedAt.ToUniversalTime().ToString("R"));
    }
}

您可能必须指定在DateTime.Parse的格式。

参考文献:

  • HTTP状态码
  • HTTP头

Disclamer:我不知道,如果ASP.NET/MVC3支持您管理Last-Modified自己。

更新

你可以创建一个扩展方法:

public static class CacheExtensions
{
    public static bool IsModified(this Controller controller, DateTime updatedAt)
    {
        var headerValue = controller.Request.Headers['If-Modified-Since'];
        if (headerValue != null)
        {
            var modifiedSince = DateTime.Parse(headerValue).ToLocalTime();
            if (modifiedSince >= updatedAt)
            {
                return false;
            }
        }

        return true;
    }

    public static ActionResult NotModified(this Controller controller)
    {
        return new HttpStatusCodeResult(304, "Page has not been modified");
    }   
}

然后用它们像这样:

public class YourController : Controller
{
    public ActionResult MyPage(string id)
    {
        var entity = _db.Get(id);
        if (!this.IsModified(entity.UpdatedAt))
            return this.NotModified();

        // page has been changed.
        // generate a view ...

        // .. and set last modified in the date format specified in the HTTP rfc.
        Response.AddHeader('Last-Modified', entity.UpdatedAt.ToUniversalTime().ToString("R"));
    }
}


Answer 2:


更新:检查我的新的答案


如何以及在哪里可以包括在MVC?

内置OutputCache过滤器做这项工作对你和它使用的标题进行缓存。 该OuputCache过滤器使用Last-Modified头,当你设置的Location作为ClientServerAndClient

[OutputCache(Duration = 60, Location = "Client")]
public ViewResult PleaseCacheMe()
{
    return View();
}

什么是包括它的优势是什么?

利用客户端缓存与条件缓存刷新

我想一个例子头怎么最后修改可以包含在一个MVC项目,静态网页和数据库查询呢?

此链接包含足够的信息来尝试一个样本。 对于像HTML静态页面,图片IIS将采取设置/检查的护理Last-Modified头和它使用的文件的最后修改日期。 对于数据库查询,你可以去设置SqlDependencyOutputCache

它是对的输出缓存不同,如果是如何? 我什么时候需要包括Last-Modified头以及何时使用的输出缓存?

OutputCache是用于ASP.NET MVC实现缓存机制的行为过滤器。 有不同的方式,你可以使用执行缓存OutputCache :客户端缓存,服务器端缓存。 Last-Modified标头是实现高速缓存在客户端的一种方法。 OutputCache过滤器使用它,当你设置的Location作为Client

如果你去为客户端缓存( Last-ModifiedETag )浏览器的缓存将自动获得在后续请求更新,你不需要做F5。



Answer 3:

上次修改对的OutputCache

该属性的OutputCache你的IIS web服务器控件输出缓存。 这是供应商特定的服务器功能(参见配置IIS 7输出缓存 )。 我也建议读缓存探索ASP.NET MVC3 ,如果你有兴趣在此技术的强大功能。

Last-Modified响应报头和它的对应的If-Modified-Since请求头是验证缓存概念(截面的代表高速缓存控制 )。 这些头是在HTTP协议的一部分,并且在指定的rfc4229

的OutputCache和验证是不是排他性的,你可以结合起来。

什么缓存场景让我开心?

像往常一样:它依赖。

100次点击/第二页上配置一个5秒的OutputCache将大大减少负荷。 使用的OutputCache,499出500次命中可以从缓存中提供(而不用花费分贝往返,计算,绘制)。

当我有服务很少立即更改,然后验证方案可以节省大量带宽的。 特别是当相比,精益304个状态消息你提供大的内容。 然而,由于每个请求验证在源的变化而变化会立即采用。

上次修改属性执行样本

根据我的经验,我建议实行验证方案(最后修订)作为动作过滤属性。 (顺便说一句: 这里的作为属性实现的其他高速缓存的情况)

从文件的静态内容

[LastModifiedCache]
public ActionResult Static()
{
    return File("c:\data\static.html", "text/html");
}

动态内容样本

[LastModifiedCache]
public ActionResult Dynamic(int dynamicId)
{
    // get data from your backend (db, cache ...)
    var model = new DynamicModel{
        Id = dynamivId,
        LastModifiedDate = DateTime.Today
    };
    return View(model);
}

public interface ILastModifiedDate
{
    DateTime LastModifiedDate { get; }
}

public class DynamicModel : ILastModifiedDate
{
    public DateTime LastModifiedDate { get; set; }
}

该LastModifiedCache属性

public class LastModifiedCacheAttribute : ActionFilterAttribute 
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is FilePathResult)
        {
            // static content is served from file in my example
            // the last file write time is taken as modification date
            var result = (FilePathResult) filterContext.Result;
            DateTime lastModify = new FileInfo(result.FileName).LastWriteTime;

            if (!HasModification(filterContext.RequestContext, lastModify))
                filterContext.Result = NotModified(filterContext.RequestContext, lastModify);
            SetLastModifiedDate(filterContext.RequestContext, lastModify);
        }

        if (filterContext.Controller.ViewData.Model is HomeController.ILastModifiedDate)
        {
            // dynamic content assumes the ILastModifiedDate interface to be implemented in the model
            var modifyInterface = (HomeController.ILastModifiedDate)filterContext.Controller.ViewData.Model;
            DateTime lastModify = modifyInterface.LastModifiedDate;

            if (!HasModification(filterContext.RequestContext, lastModify))
                filterContext.Result = NotModified(filterContext.RequestContext, lastModify);
            filterContext.RequestContext.HttpContext.Response.Cache.SetLastModified(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");
    }
}

如何启用全局上次更改suppport

您可以将LastModifiedCache属性添加到您的global.asax.cs的RegisterGlobalFilters部分全局启用在你的MVC项目这种类型的缓存。

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    ...
    filters.Add(new LastModifiedCacheAttribute());
    ...
}


Answer 4:

需要注意的是输出缓存是不是你唯一的选择在这里,事实上,你可能不希望在处理它的方式最后一次修改。 要澄清几个选项:

选项1 -使用[的OutputCache]

在这种情况下,框架将按照您指定的任何时间缓存响应体。 它将与上次修改设置为当前时间,和max-age设置为它服务于剩余直到最初的缓存持续时间到期的时间。 如果客户端发送与请求的If-Modified-此后框架将正确地返回304一旦缓存的响应然后到期的最后修改日期将每一个新的响应被缓存时进行更新。

  • 优点:缓存发生在控制器级别(因此可以针对部分内容或在不同的端部的URL相同的缓存内容工作)。 你有过缓存能力更好的控制 - 例如HttpCacheability.ServerAndPrivate让你的服务器缓存的内容,而不是中间代理。
  • 缺点:你有过最后一次修改的控制。 当你的缓存过期的所有客户端都需要重新下载,即使它实际上并没有更改的内容

选项2 -指定的Response.Cache设置

asp.net具有System.Web.OutputCacheModule的形式,其所有的请求通过outputcacheattribute以外缓存的另一个层。 这立即如同一个HTTP缓存在应用程序的前面。 所以,如果你设置合理的缓存头,不应用OutputCacheAttribute那么你的反应会在这里,而不是被缓存。 例如:

Response.Cache.SetLastModified(lastModifiedDate); Response.Cache.SetCacheability(HttpCacheability.Public); Response.Cache.SetExpires(DateTime.Now + timespan);

基于以上,则outputcachemodule将缓存的内容和相同的URL的任何请求会从缓存中提供。 请求与IF-Modified-Since的会得到304S。 (你可以使用ETag同样)。 当你的缓存过期的下一个请求,通常会击中你的应用程序,但如果你知道的内容并没有改变你以前一样,你可以返回相同的Last-Modified或ETag的。 一旦下一个响应已经被缓存,然后后续的客户届时可不必重新下载内容延长其寿命的缓存

  • 优点:如果你有确定最后修改或ETag的的一种有意义的方式,那么你必须在它的完全控制,可以减少重复下载的数量。
  • 缺点:缓存只有在请求/ URL级别。 只有当你很高兴设置缓存控制工程:公共

在(服务器)缓存过期之后将正常供应,导致200的第一个请求,即使304将是适当的 - 虽然这个选项减少了不必要的内容下载的可能性,但并不能完全消除。 这可能是最好的,虽然,因为它使缓存抢,当它以前过期它会丢弃响应主体的全新副本,因此将来的请求可以直接从缓存中提供。 我相信,HTTP缓存在理论上可以比这更聪明和使用304S延长自己的缓存生存,但asp.net一个似乎不支持。

(编辑在上面的代码中的setExpires更换SetMaxAge - 似乎IIS / asp.net不会尊重最大年龄头,除非你也SetSlidingExpiration(真),但设置将出现,以防止我们希望缓存)



Answer 5:

这是我做的缓存,并颇有些研究之后第二个答案OutputCache

让我先回答你的第二个问题。

什么是包括它的优势是什么?

浏览器缓存从服务器返回的响应。 缓存主要通过三个头控制: Cache-ControlLast-ModifiedExpires (也有其他类似ETag也来玩)。

Last-Modified头告诉浏览器的时候是资源本身已经在最后修改时间。 资源可以是静态文件动态创建的视图 。 当浏览器发出该资源与服务器进行检查请求“嘿,我已经有这个请求的响应,它是Last-Modified日期是某某..看到用户已经厌倦了...如果你返回304我很高兴使用从我的缓存响应否则请把你的新的响应快”。 (请注意,浏览器传递Last-Modified值在一个名为新标题先前由服务器返回If-Modified-Since

理想情况下,服务器应该读值了If-Modified-Since头部和必须检查与当前的修改日期,如果它们是相同的,那么它应该返回304(不修改),或者它应该返回该资源的新副本再次通过在当前的修改日期Last-Modified标头。

其优点是浏览器缓存 。 通过利用浏览器的缓存服务器可避免创建重复的响应并且如果在浏览器中缓存的响应看起来像老可返回一个新的响应。 最终的目标是节省时间

如何以及在哪里可以包括在MVC?

在如图像,HTML文件和其他静态资源的情况下,你不必担心设置如何以及在何处 ,因为IIS采取这项工作的关心 。 IIS使用文件的最后修改日期为Last-Modified标头值。

在动态页面就像通过MVC动作返回的HTML内容的情况下,你怎么能确定Last-Modified标头值? 动态驱动页面大多是数据驱动的,它是我们的责任,以决定是否响应返回以前是陈旧的或没有。

比方说,你有一个博客,你的页面是否显示的文章(不是任何其他细节),那么该页面的版本是由最后修改日期决定或创建日期的详细信息(如果文章没有被修改还)文章。 所以,你必须做它提供的视图相应的动作 @jgauffin回答同样的工作。

你问的评论我应该包括它在每个控制器动作

如果你能能够从阅读的行动从数据库的最后修改日期,那么你可以通过一个动作过滤器,避免整个动作重复的代码完成这项工作抽象出来的逻辑。 问题是你如何去从抽象的行动的细节了吗? 像传递表/列名的属性? 你要看着办吧!

举个例子..

[LastModifiedCacheFilter(Table = "tblArticles", Column = "last_modified")]
public ViewResult Post(int postId)
{
   var post = ... get the post from database using the postId
   return View(post);
}

伪代码(指我没有测试此:)的的LastModifiedCacheFilterAttribute如下所示实现使用表/列读取的最后修改日期,但它可能是一些其他的方式为好。 我们的想法是在OnActionExecuting方法我们正在做的检查并返回一个304(如果缓存仍然是新鲜的),并在OnResultExecuted方法,我们正在读/设置的最新修改日期。

public class LastModifiedCacheFilterAttribute : ActionFilterAttribute
{
    // Could be some other things instead of Table/Column
    public string Table { get; set; }
    public string Column { get; set; }    

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
      // var lastModified = read the value from the passed Column/Table and set it here 

      var ifModifiedSinceHeader = filterContext.RequestContext.HttpContext.Request.Headers["If-Modified-Since"];

      if (!String.IsNullOrEmpty(ifModifiedSinceHeader))
      {
        var modifiedSince = DateTime.Parse(ifModifiedSinceHeader).ToLocalTime();
        if (modifiedSince >= lastModified)
        {
          filterContext.Result = new EmptyResult();
          filterContext.RequestContext.HttpContext.Response.Cache.SetLastModified(lastModified.ToUniversalTime());
          filterContext.RequestContext.HttpContext.Response.StatusCode = 304;
        }
      }

      base.OnActionExecuting(filterContext);
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
      // var lastModified = read the value from the passed Column/Table and set it herefilterContext.RequestContext.HttpContext.Response.Cache.SetLastModified(lastModified.ToUniversalTime());
      base.OnResultExecuted(filterContext);
    }
}

为什么不能的OutputCache属性?

按我的分析, OutputCache属性不使用Last-Modified缓存机制。 另一件事是它使用了旧的页面缓存机制,使得它很难定制/扩展。

你真的需要实现一切行动的最后一次修改的机制

真的不是必需的。 你可以实现的最后修改机制 ,这需要更多的时间来建立这样一个响应的动作,它需要更多的时间去旅行沿着电线的响应和到达浏览器。 在其他情况下,我觉得这只是一个开销在所有的行动实现,你也必须测量出才这样做的好处 。 其他的主要观点是,在许多情况下,页面的版本不只是一个单一的表列可能是由许多其他事情决定 ,并在这些情况下,它可能会更复杂,实现这个!

围绕一个点ETag

虽然问题是有关Last-Modified头,我应该告诉一些有关ETag点击发表您的应答按钮之前。 相比于Last-Modified (其依赖于日期时间)报头ETag报头(依赖于一个散列值)是决定在浏览器中缓存的响应是新鲜的还是不更准确的,但它可能是有点复杂实现。 IIS还包括ETag头与一起Last-Modified头的静态资源。 之前实施的任何这种机制的谷歌,看看是否有出有任何库,可以帮助你!



文章来源: Last-Modified Header in MVC