这是我在对缓存和OutputCache 做了一些相当研究之后的第二个答案。
让我先回答你的第二个问题。
包含它有什么好处?
浏览器缓存从服务器返回的响应。缓存主要由三个标头控制:Cache-Control、Last-Modified 和Expires(还有其他类似ETag 的标头也可以使用)。
Last-Modified 标头告诉浏览器资源最后何时被修改。 资源可以是静态文件或动态创建的视图。每当浏览器向该资源发出请求时,它都会与服务器进行检查您返回 304 我很高兴使用缓存中的响应,否则请快速发送您的新响应”。 (请注意,浏览器将服务器先前返回的 Last-Modified 值传递到名为 If-Modified-Since 的新标头中)
理想情况下,服务器应该从 If-Modified-Since 标头中读取值,并且必须检查当前修改日期,如果它们相同,那么它应该返回 304 (NOT MODIFIED) 或者它应该返回资源的新副本再次通过 Last-Modified 标头中的当前修改日期。
优点是浏览器缓存。通过利用浏览器缓存,服务器可以避免创建重复响应,并且如果浏览器中的缓存响应看起来很旧,它还可以返回新响应。最终目标是节省时间。
如何以及在哪里可以将它包含在 MVC 中?
对于图像、html 文件等静态资源,您无需担心设置 How 和 Where,因为 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 属性没有使用Last-Modified 缓存机制。另一件事是它使用旧的页面缓存机制,难以自定义/扩展。
您真的需要在所有操作中实现最后修改机制吗?
真的不需要。您可以对需要更多时间来创建此类响应并且需要更多时间来移动响应的操作实施上次修改机制沿着电线到达浏览器。在其他情况下,我觉得这只是在所有操作中实施的开销,而且您必须在这样做之前衡量收益。另一个要点是,在许多情况下,页面的版本不仅仅由单个表列决定,它可能由许多其他因素决定,在这些情况下,实现这一点可能会更复杂!
关于ETag的一点
虽然问题是关于Last-Modified 标头,但在点击发布您的答案 按钮之前,我应该先介绍一下ETag。与Last-Modified(依赖于日期时间)标头相比,ETag 标头(依赖于哈希值)在确定浏览器中的缓存响应是否新鲜方面更准确,但实现起来可能有点复杂。 IIS 还包括ETag 标头以及用于静态资源的Last-Modified 标头。在实施任何这种机制之前,请先搜索一下是否有任何库可以帮助您!