Подтвердить что ты не робот

Последний измененный заголовок в MVC

Недавно я столкнулся с последним измененным заголовком.

  • Как и где я могу включить его в MVC?
  • В чем преимущества его включения?

Мне нужен пример того, как последний измененный заголовок может быть включен в проект mvc, для статических страниц и запросов к базе данных?

Отличается ли он от outputcache, если да, как?

В принципе, я хочу, чтобы браузер очистил кеш и отобразил последние данные или страницы автоматически, без необходимости для пользователя обновлять или очищать кеш.

4b9b3361

Ответ 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.

Литература:

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

Ответ 2


ОБНОВЛЕНИЕ: проверьте мой новый ответ


Как и где я могу включить его в MVC?

Встроенный фильтр OutputCache выполняет задание для вас, и он использует эти заголовки для кэширования. Фильтр OuputCache использует заголовок Last-Modified, когда вы устанавливаете Location как Client или ServerAndClient.

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

Каковы преимущества его включения?

Использование кэширования на стороне клиента с флагом условного кэша

Мне нужен пример того, как последний измененный заголовок может быть включен в проект mvc, для статических страниц и запросов к базе данных?

Эта ссылка содержит достаточно информации, чтобы опробовать образец. Для статических страниц, таких как html, изображения IIS позаботится о настройке/проверке заголовка Last-Modified, и он использует дату последнего изменения файла. Для запросов к базе данных вы можете выбрать установку SqlDependency в OutputCache.

Разве это отличается от outputcache, если да, как? Когда мне нужно включить Last-Modified Header и когда использовать outputcache?

OutputCache - это фильтр действий, используемый для реализации механизма кэширования в ASP.NET MVC. Существуют различные способы кэширования с помощью OutputCache: кэширование на стороне клиента, кеширование на стороне сервера. Заголовок Last-Modified - это один из способов выполнить кеширование на стороне клиента. Фильтр OutputCache использует его, когда вы устанавливаете Location как Client.

Если вы переходите на кеширование на стороне клиента (Last-Modified или ETag), кеш браузера автоматически будет обновляться в последующем запросе, и вам не нужно делать F5.

Ответ 3

Last-Modified vs. OutputCache

Атрибут OutputCache контролирует кэширование вывода на вашем IIS WebServer. Это особенность сервера поставщика (см. Настройка кэширования вывода IIS 7). Я также предлагаю читать Cache Exploration в ASP.NET MVC3, если вас интересуют мощные возможности этой технологии.

Last-Modified ответный заголовок If-Modified-Since - это представители концепции кеширования валидации (раздел управление кешем). Эти заголовки являются частью протокола HTTP и указаны в rfc4229

OutputCache и проверка не являются исключительными, вы можете объединить их.

Какой сценарий кэширования меня радует?

Как обычно: это зависит.

Настройка 5-секундного OutputCache на странице 100 ударов в секунду резко снизит нагрузку. Используя OutputCache, 499 из 500 обращений могут быть отправлены из кеша (и не стоят db roundtrip, вычислений, рендеринга).

Когда мне приходится редко выполнять изменения, то сценарий проверки может сэкономить много полос пропускания. Специально, когда вы обслуживаете большой контент по сравнению с сообщением о состоянии постного 304. Тем не менее, изменения принимаются немедленно, поскольку каждый запрос проверяет наличие изменений в источнике.

Пример реализации последней модификации атрибута

Основываясь на моем опыте, я бы рекомендовал реализовать сценарий проверки (последний модифицированный) как атрибут фильтра действий. (Btw: Здесь другой сценарий кэширования, реализованный как атрибут)

Статический контент из файла

[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 LastModified

Вы можете добавить атрибут LastModifiedCache в раздел RegisterGlobalFilters вашего global.asax.cs, чтобы глобально включить этот тип кэширования в вашем проекте mvc.

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

Ответ 4

Обратите внимание, что outputcache не является вашим единственным вариантом здесь, и на самом деле вы, возможно, не хотите обрабатывать измененные в последний раз так, как это делается. Чтобы прояснить несколько вариантов:

Вариант 1 - используйте [OutputCache]

В этом случае структура будет кэшировать тело ответа в соответствии с любой продолжительностью, указанной вами. Он будет обслуживать его с помощью параметра Last-Modified, установленного в текущее время, и максимального возраста, установленного на время, оставшееся до истечения первоначальной продолжительности кеша. Если клиент отправляет запрос с If-Modified-Since, тогда структура будет правильно возвращать значение 304. После истечения срока действия кэшированного ответа дата Last-Modified будет обновляться каждый раз, когда новый ответ будет кэшироваться.

  • Плюсы: кэширование происходит на уровне контроллера (поэтому может работать для частичного контента или одного и того же кэшированного контента на разных конечных URL-адресах). Вам лучше контролировать кешируемость - например, HttpCacheability.ServerAndPrivate позволяет серверу кэшировать контент, но не промежуточные прокси.
  • Минусы: у вас нет контроля над последними изменениями. Когда срок действия вашего кеша истекает, все клиенты должны будут повторно загрузить контент, даже если он фактически не изменился.

Вариант 2 - укажите настройки в Response.Cache

asp.net имеет еще один уровень кэширования вне атрибута outputcache в виде System.Web.OutputCacheModule, через который проходят все запросы. Это ведет себя как HTTP-кеш непосредственно перед вашим приложением. Поэтому, если вы установите разумные заголовки кеша без применения OutputCacheAttribute, тогда вместо этого будет кэшироваться ваш ответ. Например:

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

Исходя из вышесказанного, выходной файл будет кэшировать ваш контент, и любые запросы к одному и тому же URL-адресу будут отправлены из кеша. Запросы с If-Modified-Since получат 304s. (Вы можете использовать ETags одинаково). По истечении срока действия вашего кеша следующий запрос попадет в ваше приложение, но если вы знаете, что контент не изменился, вы можете вернуть тот же Last-Modified или ETag, что и ранее. После того как этот следующий ответ будет кэширован, последующие клиенты смогут продлить срок службы кеша без повторной загрузки содержимого.

  • Плюсы: Если у вас есть осмысленный способ определения последнего изменения или ETag, тогда вы полностью контролируете его и можете уменьшить количество дублированных загрузок.
  • Минусы: кеширование доступно только на уровне запроса /URL. Работает только в том случае, если вы готовы установить кеш-контроль: public

Несмотря на то, что эта опция уменьшает вероятность ненужной загрузки контента, она не устраняет ее - первый запрос после истечения срока действия сервера (сервера) будет обслуживаться нормально и приведет к 200, даже если 304 было бы подходящим. Это, вероятно, самое лучшее, поскольку, поскольку он позволяет кешу захватывать новую копию тела ответа, которую он бы отбросил, когда он истек раньше, и, следовательно, будущие запросы могут быть отправлены прямо из кеша. Я полагаю, что кеширование HTTP в теории может быть более умным, чем это, и использовать 304s для продления собственного времени кеша, но asp.net, похоже, не поддерживает этого.

(Отредактировано для замены SetMaxAge с помощью SetExpires в приведенном выше коде, похоже, что IIS/asp.net не будет уважать заголовки максимального возраста, если вы также не задаете SetSlidingExpiration (true), но этот параметр указывает на то, что мы не хотим, чтобы мы использовали кеширование)

Ответ 5

Это мой второй ответ, сделав некоторое исследование кеширования и OutputCache.

Позвольте мне сначала ответить на ваш второй вопрос.

В чем преимущества его включения?

Браузер кэширует ответы, возвращаемые с сервера. Кэширование контролируется в основном тремя заголовками: Cache-Control, Last-Modified и Expires (есть и другие, такие как ETag).

Заголовок Last-Modified сообщает браузеру, когда последний ресурс был изменен. Ресурсом может быть либо статический файл, либо динамически созданный вид. Всякий раз, когда браузер делает запрос на этот ресурс, он проверяет с сервером "Эй, у меня уже есть ответ на этот запрос, а дата Last-Modified - это так и так.. видеть, что пользователь уже устал... если вы вернете 304 Я рад использовать ответ из моего кеша, пожалуйста, быстро отправьте свой новый ответ". (Обратите внимание, что браузер передает значение Last-Modified, возвращенное ранее сервером в новом заголовке If-Modified-Since)

В идеале сервер должен считывать значение из заголовка If-Modified-Since и должен проверяться с текущей измененной датой, и если они одинаковы, тогда он должен возвращать 304 (НЕ ИЗМЕНЕН) или он должен вернуться новая копия ресурса, снова передающая текущую измененную дату в заголовке Last-Modified.

Преимущество кеширования браузера. Используя кеширование браузера, сервер может избежать создания дублирующего ответа, а также может возвращать новый ответ, если кешированный ответ в браузере выглядит как старый. Конечная цель сохранить время.

Как и где я могу включить его в MVC?

В случае статических ресурсов, таких как изображения, html файлы и другие, вам не нужно беспокоиться о настройке Как и Где, потому что IIS заботится о это работа. IIS использует дату последнего изменения файла как значение заголовка Last-Modified.

В случае динамических страниц, таких как содержимое html, возвращаемое посредством действия MVC, как вы можете определить значение заголовка 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 (который полагается на datetime) заголовок заголовка ETag (зависит от значения хэша) более точно определяет, является ли кешированный ответ в браузере свежим или нет, но это может быть немного сложно реализовать. IIS также включает заголовок ETag вместе с заголовком Last-Modified для статических ресурсов. Прежде чем внедрять какой-либо из этих механизмов google и посмотреть, есть ли там какая-нибудь библиотека, которая поможет вам!