web-dev-qa-db-ja.com

MVCの最終変更ヘッダー

最近、Last-Modifiedヘッダーを見つけました。

  • MVCに含める方法と場所を教えてください。
  • それを含めることの利点は何ですか?

静的ページとデータベースクエリについても、最後に変更されたヘッダーをmvcプロジェクトに含める方法の例が必要ですか?

もしそうなら、それはoutputcacheとは異なりますか?

基本的に、ユーザーがキャッシュを更新したりクリアしたりせずに、ブラウザでキャッシュをクリアして最新のデータやページを自動的に表示したいと思います。

40
learning

Last-Modifiedは主にキャッシュに使用されます。変更時間を追跡できるリソースに対して送信されます。リソースはファイルでなくてもかまいません。たとえば、UpdatedAt列があるdB情報から生成されたページ。

これは、各ブラウザがリクエストで送信するIf-Modified-Sinceヘッダーと組み合わせて使用​​されます(以前にLast-Modifiedヘッダーを受信したことがある場合)。

MVCに含める方法と場所を教えてください。

Response.AddHeader

それを含めることの利点は何ですか?

動的に生成されるページのきめの細かいキャッシュを有効にします(たとえば、DBフィールド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"));
    }
}
47
jgauffin


更新:新しい答えを確認してください


MVCに含める方法と場所はどこですか?

組み込みのOutputCacheフィルターがその役目を果たし、それらのヘッダーをキャッシュに使用します。 OuputCacheフィルターをLocationClientまたはServerAndClientとして設定すると、Last-Modifiedヘッダーが使用されます。

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

それを含めることの利点は何ですか?

条件付きキャッシュフラッシュによるクライアント側キャッシングの活用

静的ページとデータベースクエリの場合も、最後に変更されたヘッダーをmvcプロジェクトに含める方法の例が必要ですか?

この link には、サンプルを試すのに十分な情報が含まれています。 htmlのような静的ページの場合、イメージIISはLast-Modifiedヘッダーの設定/チェックを処理し、ファイルの最終変更日を使用します。データベースクエリの場合、SqlDependencyを設定できますOutputCache内。

出力キャッシュの場合は異なりますか? Last-Modifiedヘッダーを含める必要があるのはいつですか、そしてoutputcacheをいつ使用するのですか?

OutputCacheは、ASP.NET MVCでキャッシングメカニズムを実装するために使用されるアクションフィルターです。 OutputCacheを使用してキャッシュを実行する方法はいくつかあります。クライアント側のキャッシュ、サーバー側のキャッシュです。 Last-Modifiedヘッダーは、クライアント側でキャッシングを実現する1つの方法です。 OutputCacheフィルターは、LocationClientとして設定するときに使用します。

クライアント側のキャッシュ(Last-ModifiedまたはETag)を使用する場合、ブラウザーのキャッシュは後続のリクエストで自動的に更新されるため、F5を実行する必要はありません。

18
VJAI

最終変更とOutputCache

OutputCache属性は、IIS WebServerの出力キャッシュを制御します。これはベンダー固有のサーバー機能です( IIS 7出力キャッシュ)を構成する 。このテクノロジの強力な機能に興味がある場合は、 ASP.NET MVC3のキャッシュ探索 もお勧めします。 。

Last-Modified応答ヘッダーと対応するIf-Modified-Sinceリクエストheaderは、検証キャッシュの概念の代表です(セクション cache control )。これらのヘッダーはHTTPプロトコルの一部であり、 rfc4229 で指定されています

OutputCacheと検証は排他的ではなく、組み合わせることができます。

どのキャッシュシナリオが私を幸せにしますか?

いつものように:それは異なります。

100ヒット/秒のページで5秒のOutputCacheを構成すると、負荷が大幅に削減されます。 OutputCacheを使用すると、500ヒットのうち499をキャッシュから提供できます(dbラウンドトリップ、計算、レンダリングのコストはかかりません)。

まれにすぐに変更を提供する必要がある場合は、検証シナリオによって帯域幅を大幅に節約できます。特に、リーン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");
    }
}

LastModifiedグローバルサポートを有効にする方法

LastModifiedCache属性をglobal.asax.csのRegisterGlobalFiltersセクションに追加して、mvcプロジェクトでこのタイプのキャッシュをグローバルに有効にすることができます。

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

ここでは、outputcacheが唯一のオプションではないことに注意してください。実際には、最終変更を処理する方法で処理したくない場合があります。いくつかのオプションを明確にするには:

オプション1-[OutputCache]を使用

この場合、フレームワークは、指定した期間に従って応答本文をキャッシュします。 Last-Modifiedを現在の時刻に設定し、max-ageを元のキャッシュ期間が終了するまでの残り時間に設定してサービスを提供します。クライアントがIf-Modified-Sinceでリクエストを送信した場合、フレームワークは304を正しく返します。キャッシュされた応答が期限切れになると、Last-Modifiedの日付は新しい応答がキャッシュされるたびに更新されます。

  • 長所:キャッシュはコントローラーレベルで行われます(したがって、部分的なコンテンツや、異なるエンド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を含むリクエストは304秒になります。 (ETagを同等に使用できます)。キャッシュが期限切れになると、次のリクエストは通常​​どおりアプリにヒットしますが、コンテンツが変更されていないことがわかっている場合は、以前と同じLast-ModifiedまたはETagを返すことができます。この次の応答がキャッシュされると、後続のクライアントは、コンテンツを再ダウンロードすることなく、キャッシュの寿命を延ばすことができます。

  • 長所:最終変更またはETagを決定する意味のある方法がある場合は、それを完全に制御でき、重複ダウンロードの数を減らすことができます。
  • 短所:キャッシュはリクエスト/ URLレベルでのみです。 cache-controlを設定して問題がない場合にのみ機能します:public

このオプションは、不要なコンテンツのダウンロードの可能性を減らしますが、それを排除するわけではありません-(サーバー)キャッシュの有効期限が切れた後の最初のリクエストは正常に処理され、たとえ304が適切だったとしても200になります。これにより、キャッシュが応答本文の新しいコピーを取得できるようになるため、これはおそらく最良のものです。 HTTPキャッシュは理論的にはこれよりも賢く、独自のキャッシュの有効期間を延長するために304を使用できると思いますが、asp.netはそれをサポートしていないようです。

(上記のコードでSetMaxAgeをSetExpiresに置き換えるように編集されています。IIS/ asp.netは、SetSlidingExpiration(true)も含めない限り、max-ageヘッダーを尊重しないようですが、その設定は、必要なキャッシュを防ぐように見えます)

4
Gareth

これは、キャッシングとOutputCacheに関するかなりの調査を行った後の2番目の回答です。

最初に2つ目の質問にお答えしましょう。

それを含めることの利点は何ですか?

ブラウザはサーバーから返された応答をキャッシュします。キャッシングは、主にCache-ControlLast-Modified、およびExpiresの3つのヘッダーによって制御されます(ETagのような他のヘッダーもあります)。

Last-Modifiedヘッダーは、[リソースが最後に変更されたときをいつブラウザーに通知するかを示します。 リソース静的ファイルまたは動的に作成されたビューのいずれかです。ブラウザがそのリソースを要求するたびに、サーバーでチェックします"ねえ、私はすでにこの要求に対する応答があり、それはLast-Modifiedの日付がそうであるということです。ユーザーがすでに疲れているのを確認してください。 .. 304を返す場合は、キャッシュからの応答を使用できて嬉しいです。それ以外の場合は、新しい応答をすばやく送信してください]。 (ブラウザは、以前にサーバーから返されたLast-Modified値をIf-Modified-Sinceという新しいヘッダーに渡します。)

理想的には、サーバーはIf-Modified-Sinceヘッダーから値を読み取り、現在の変更日を確認する必要があり、それらが同じ場合は4(NOT MODIFIED)を返すか、またはリソースの新しいコピーは、Last-Modifiedヘッダーで現在の変更日を再び渡します。

利点はブラウザキャッシングです。ブラウザーのキャッシュを活用することで、サーバーは重複した応答の作成を回避でき、ブラウザーにキャッシュされた応答が古いように見える場合、新しい応答を返すこともできます。最終的な目標は時間を節約です。

MVCに含める方法と場所を教えてください。

画像、htmlファイル、その他の静的リソースの場合、HowWhereの設定について心配する必要はありません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実装の疑似コード(私がこれをテストしていないことを意味します)は、テーブル/列を使用して最終変更日を読み取りますが、他の方法も考えられます。アイデアは、チェックを行って304(キャッシュがまだ新しい場合)を返すOnActionExecutingメソッドにあり、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キャッシングメカニズムを使用していません。もう1つは、古いページキャッシュメカニズムを使用しているため、カスタマイズや拡張が困難なことです。

本当にすべてのアクションに最終変更メカニズムを実装する必要がありますか?

本当に必要ありません。 最後に変更されたメカニズムを実行するアクションにそのような応答を作成するためのより多くの時間を実装できますより多くの移動時間応答を実行します回線を切断してブラウザにアクセスします。他の場合では、私はそれがすべてのアクション全体で実装するオーバーヘッドであると感じ、またそうする前にメリットを測定するする必要があります。他の主なポイントは、多くの場合、ページのバージョンは単一のテーブル列だけで決定されるわけではありません他の多くのことによる可能性があり、これらの場合、これを実装するのはより複雑になる可能性があります。

ETagに関するポイント

質問はLast-Modifiedヘッダーに関するものですが、Post Your Answerボタンをクリックする前に、ETagについて何か説明する必要があります。 Last-Modified(datetimeに依存)ヘッダーと比較して、ETagヘッダー(ハッシュ値に依存)は、ブラウザーでキャッシュされた応答が新しいかどうかを判断するのにより正確ですが、少し複雑になる可能性があります実装する。 IISには、静的リソースのLast-ModifiedヘッダーとともにETagヘッダーも含まれています。このメカニズムを実装する前に、google outして、そこにライブラリがあるかどうかを確認してくださいあなたを助けます!

1
VJAI