最近、Last-Modifiedヘッダーを見つけました。
静的ページとデータベースクエリについても、最後に変更されたヘッダーをmvcプロジェクトに含める方法の例が必要ですか?
もしそうなら、それはoutputcacheとは異なりますか?
基本的に、ユーザーがキャッシュを更新したりクリアしたりせずに、ブラウザでキャッシュをクリアして最新のデータやページを自動的に表示したいと思います。
Last-Modified
は主にキャッシュに使用されます。変更時間を追跡できるリソースに対して送信されます。リソースはファイルでなくてもかまいません。たとえば、UpdatedAt
列があるdB情報から生成されたページ。
これは、各ブラウザがリクエストで送信するIf-Modified-Since
ヘッダーと組み合わせて使用されます(以前にLast-Modified
ヘッダーを受信したことがある場合)。
MVCに含める方法と場所を教えてください。
それを含めることの利点は何ですか?
動的に生成されるページのきめの細かいキャッシュを有効にします(たとえば、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"));
}
}
更新:新しい答えを確認してください
MVCに含める方法と場所はどこですか?
組み込みのOutputCache
フィルターがその役目を果たし、それらのヘッダーをキャッシュに使用します。 OuputCache
フィルターをLocation
をClient
または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
フィルターは、Location
をClient
として設定するときに使用します。
クライアント側のキャッシュ(Last-Modified
またはETag
)を使用する場合、ブラウザーのキャッシュは後続のリクエストで自動的に更新されるため、F5を実行する必要はありません。
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());
...
}
ここでは、outputcacheが唯一のオプションではないことに注意してください。実際には、最終変更を処理する方法で処理したくない場合があります。いくつかのオプションを明確にするには:
オプション1-[OutputCache]を使用
この場合、フレームワークは、指定した期間に従って応答本文をキャッシュします。 Last-Modifiedを現在の時刻に設定し、max-ageを元のキャッシュ期間が終了するまでの残り時間に設定してサービスを提供します。クライアントがIf-Modified-Sinceでリクエストを送信した場合、フレームワークは304を正しく返します。キャッシュされた応答が期限切れになると、Last-Modifiedの日付は新しい応答がキャッシュされるたびに更新されます。
オプション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を返すことができます。この次の応答がキャッシュされると、後続のクライアントは、コンテンツを再ダウンロードすることなく、キャッシュの寿命を延ばすことができます。
このオプションは、不要なコンテンツのダウンロードの可能性を減らしますが、それを排除するわけではありません-(サーバー)キャッシュの有効期限が切れた後の最初のリクエストは正常に処理され、たとえ304が適切だったとしても200になります。これにより、キャッシュが応答本文の新しいコピーを取得できるようになるため、これはおそらく最良のものです。 HTTPキャッシュは理論的にはこれよりも賢く、独自のキャッシュの有効期間を延長するために304を使用できると思いますが、asp.netはそれをサポートしていないようです。
(上記のコードでSetMaxAgeをSetExpiresに置き換えるように編集されています。IIS/ asp.netは、SetSlidingExpiration(true)も含めない限り、max-ageヘッダーを尊重しないようですが、その設定は、必要なキャッシュを防ぐように見えます)
これは、キャッシングとOutputCache
に関するかなりの調査を行った後の2番目の回答です。
最初に2つ目の質問にお答えしましょう。
それを含めることの利点は何ですか?
ブラウザはサーバーから返された応答をキャッシュします。キャッシングは、主にCache-Control
、Last-Modified
、およびExpires
の3つのヘッダーによって制御されます(ETag
のような他のヘッダーもあります)。
Last-Modified
ヘッダーは、[リソースが最後に変更されたときをいつブラウザーに通知するかを示します。 リソースは静的ファイルまたは動的に作成されたビューのいずれかです。ブラウザがそのリソースを要求するたびに、サーバーでチェックします"ねえ、私はすでにこの要求に対する応答があり、それはLast-Modified
の日付がそうであるということです。ユーザーがすでに疲れているのを確認してください。 .. 304を返す場合は、キャッシュからの応答を使用できて嬉しいです。それ以外の場合は、新しい応答をすばやく送信してください]。 (ブラウザは、以前にサーバーから返されたLast-Modified
値をIf-Modified-Since
という新しいヘッダーに渡します。)
理想的には、サーバーはIf-Modified-Since
ヘッダーから値を読み取り、現在の変更日を確認する必要があり、それらが同じ場合は4(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
実装の疑似コード(私がこれをテストしていないことを意味します)は、テーブル/列を使用して最終変更日を読み取りますが、他の方法も考えられます。アイデアは、チェックを行って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して、そこにライブラリがあるかどうかを確認してくださいあなたを助けます!