数時間の調査の後、この問題を解決するための次の方法が見つかりました。
1)キャッシュするページの「OutputCache」ディレクティブの宣言宣言。
<%@ OutputCache Duration="600" VaryByParam="*" VaryByCustom="custom" %>
2)“ coudeBehind”ページでResponse.Cacheオブジェクトのキャッシュパラメーターを設定します。
HttpCachePolicy policy = Response.Cache; policy.SetCacheability(HttpCacheability.Server); policy.SetExpires(app.Context.Timestamp.AddSeconds((double)600)); policy.SetMaxAge(new TimeSpan(0, 0, 600)); policy.SetValidUntilExpires(true); policy.SetLastModified(app.Context.Timestamp); policy.VaryByParams.IgnoreParams = true;
これらのアプローチには、次のような重大な欠点があります。
- 柔軟性の欠如
- 各ページにパラメーターを設定する必要性
タスクの分析で最初に生じた欲求は、ユニバーサルキャッシュモジュールを作成することでした。
まず、新しいプロジェクトを作成する必要があります。 これにより、モジュールをライブラリとして実装できるようになります。これにより、モジュールをさまざまなプロジェクトに接続し、完全にユニバーサルにすることができます。
モジュールの実装
「IHttpModule」インターフェイスを実装するクラス「OutputCacheModule」を作成してみましょう。2つのメソッド「Dispose」と「Init」を実装します。 この場合、「Dispose」メソッドは実装せずに残しておくことができ、「Init」メソッドはより近いものと見なされます。
「Init」メソッドは「HttpApplication」を受け入れます。これにより、アプリケーションのグローバルイベントにアクセスできるようになります。これは、興味のあるいくつかのイベントをサブスクライブするときに使用する必要があります。
public void Init(HttpApplication app) { app.PreRequestHandlerExecute += new EventHandler(OnApplicationPreRequestHandlerExecute); app.PostRequestHandlerExecute += new EventHandler(OnPostRequestHandlerExecute); }
「PostRequestHandlerExecute」メソッドは、呼び出されたページまたは他のハンドラーがリクエストの処理を終了した直後に呼び出されます。 この段階では、いくつかの条件を確認し、キャッシュの必要性を判断し、必要な「ヘッダー」を追加します。
public void OnPostRequestHandlerExecute(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; Int32 duration = CacheHelper.GetCacheDuration(app.Context.Request); HttpCachePolicy policy = app.Response.Cache; if (!InverseContext.User.IsAuthenticated && duration > 0) { policy.SetCacheability(HttpCacheability.Server); policy.SetExpires(app.Context.Timestamp.AddSeconds(duration )); policy.SetMaxAge(new TimeSpan(0, 0, duration )); policy.SetValidUntilExpires(true); policy.SetLastModified(app.Context.Timestamp); policy.VaryByParams["*"] = true; } else { policy.SetCacheability(HttpCacheability.NoCache); policy.SetExpires(app.Context.Timestamp.AddSeconds(0)); } }
このアプローチにより、キャッシュ条件を柔軟に管理し、必要に応じて個々のページに必要な構成を選択できます。
さて、最初のメソッド「PreRequestHandlerExecute」に戻ります。これは、物語の直線性に違反しないように意図的にスキップしました。 「PreRequestHandlerExecute」メソッドは、呼び出されたページまたは他のハンドラーがリクエストの実行を開始する直前に呼び出されます。 そしてこの時点で、キャッシュ内のページの有効性を確認するための「コールバック」メソッドを指定する必要があります。
public void OnApplicationPreRequestHandlerExecute(object sender, EventArgs e) { HttpApplication app = (HttpApplication) sender; if (!InverseContext.User.IsAuthenticated) { app.Context.Response.Cache.AddValidationCallback(new HttpCacheValidateHandler(Validate), app); } }
このレコードは文字通り次のことを意味します-キャッシュ内のページがリクエストされるたびに、最初に「コールバック」メソッドが呼び出され、すべての必要な条件をチェックし、ページのキャッシュバージョンを返すか、新しいページ処理サイクルを開始します。 このメソッドのコードを見てみましょう:
public void Validate(HttpContext context, Object data, ref HttpValidationStatus status) { if (InverseContext.User.IsAuthenticated) { status = HttpValidationStatus.IgnoreThisRequest; context.Response.Cache.AddValidationCallback(new HttpCacheValidateHandler(Validate), "somecustomdata"); } else { status = HttpValidationStatus.Valid; } }
この例では、非常に単純な条件が実装されています。ユーザーがログインしていない場合、ユーザーはキャッシュからページを受け取ります。 ご覧のとおり、条件の数はニーズによってのみ制限されます。 ただし、この方法でも、次の点に注意する必要があります。
「HttpValidationStatus」-キャッシュされたデータのステータスを決定します。3つの値があります。
- IgnoreThisRequest-ページは再び機能しますが、キャッシュにあるバージョンは有効なままです。
- 無効-ページは再び機能し、キャッシュ内のバージョンは無効として認識され、このリクエストで解決された新しいバージョンに置き換えられます。
- 有効-キャッシュは有効です。答えはキャッシュのページのバージョンです。
テスト期間中、このページに実際に設定されたキャッシングパラメータに関するサービス情報を受信すると便利です。 これを行うには、「Init」メソッドで、「Application_EndRequest」イベントを追加でサブスクライブする必要があります。その時点で、リクエスト処理のすべての段階がすでに完了しており、理論的には誰も「ヘッダー」を変更しないためです。 情報を表示するには、同様の方法を使用できます。
private void PrintServiceInformation(HttpApplication app) { app.Context.Response.Write("Rendered at " + DateTime.Now + "<br/>"); var cacheability = app.Context.Response.Cache.GetType().GetField("_cacheability", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(app.Context.Response.Cache); app.Context.Response.Write("HttpCacheability = " + cacheability); var expires = app.Context.Response.Cache.GetType().GetField("_utcExpires", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(app.Context.Response.Cache); app.Context.Response.Write("<br/> UtcExpires = " + expires); }
ここでは反射魔法が不可欠です。 ただし、これはテスト段階のみです。 将来的には、このメソッドの使用を拒否する必要があります。
マイクロソフトの最後の魔術
1)サイトがIISサーバーを実行しているときに、このメソッドが「HttpCacheability.Private」パラメーターを指定して「SetCacheability()」メソッドを呼び出すため、キャッシングを使用する場所で「Responce.Flush()」メソッドを使用しないでください。不可能(2項を参照)。
2)ASP.Netのキャッシングパラメーターは、「HttpCacheability」を設定しようとするとメソッドがチェックするため、ほつれない
if (s_cacheabilityValues[(int)cacheability] < s_cacheabilityValues[(int)_cacheability])
「enum」は次のとおりです。
public enum HttpCacheability { NoCache = 1, Private = 2, Server = 3, ServerAndNoCache = 3, Public = 4, ServerAndPrivate = 5, }
そのため、すでに価値があるものよりも「int」値に大きな値を設定することは不可能です。 「SetExpires」メソッドも同様に機能します。