English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
大規模なウェブサイトシステムでは、システムのアクセス性能を向上させるために、変わらない内容を静的ページとして公開することがよくあります。例えば、ショッピングカートの製品詳細ページ、ニュース詳細ページなど、これらの情報が公開されると、変更される頻度は高くありません。動的な出力方式で処理を行うと、サーバーに大きなリソースの浪費が発生します。しかし、これらの内容すべてに対して独立して静的ページを作成することはできません。したがって、システム内で仮静态化の方法を使用することができます。仮静态化とは何か、百度で検索してください。ここでは、ASP.NET Core MVCで仮静态化を実現する方法について紹介します。
MVCフレームワークでは、Viewはビューを表しており、その実行結果はクライアントブラウザに最終的に出力される内容です。HTML、CSS、JSなどが含まれます。静的化を実現するためには、Viewの実行結果を静的ファイルとして保存し、指定された場所(ディスク、分散キャッシュなど)に保存する必要があります。次にアクセスすると、保存された内容を直接読み取ることができ、再度ビジネスロジックを実行する必要がありません。それでは、ASP.NET Core MVCでこのような機能を実現するにはどうすればいいのでしょうか?答えはフィルタを使用することです。MVCフレームワークでは、さまざまなフィルタータイプが提供されています。ここで使用するのはアクションフィルタです。アクションフィルタは2つのタイムポイントを提供しており、アクションが実行される前、アクションが実行される後に分かれています。アクションが実行される前に、既に静的ページが生成されているかどうかを判断し、既に生成されている場合はファイル内容を直接読み取って出力し、以降のロジックを実行をスキップします。生成されていない場合は、アクションが実行された後に結果をキャッチし、生成された静的コンテンツを保存します。
それでは具体的な実装コードに進みましょう。まず、フィルタータイプを定義します。私たちはそれをStaticFileHandlerFilterAttributeと名付けます。このクラスはフレームワークが提供するActionFilterAttributeを継承しており、StaticFileHandlerFilterAttributeは基底クラスが提供する2つのメソッドをオーバーライドしています:OnActionExecuted(アクションが実行された後)、OnActionExecuting(アクションが実行される前)。具体的なコードは以下の通りです:
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, AllowMultiple = false, Inherited = false)] public class StaticFileHandlerFilterAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext context){} public override void OnActionExecuting(ActionExecutingContext context){} }
OnActionExecutingで、静的コンテンツが既に生成されているかどうかを判断する必要があります。既に生成されている場合は直接内容を出力します。ロジックの実装は以下の通りです:
//特定のルールに従って静的ファイルの名前を生成します。ここではareaに従っています。+"-"+controller+"-"+action+Keyのルール生成 string controllerName = context.RouteData.Values["controller"].ToString().ToLower(); string actionName = context.RouteData.Values["action"].ToString().ToLower(); string area = context.RouteData.Values["area"].ToString().ToLower(); //このKeyはデフォルトでidに等しいですが、異なるKey名を設定することもできます。 string id = context.RouteData.Values.ContainsKey(Key) ? context.RouteData.Values[Key].ToString() : ""; if (string.IsNullOrEmpty(id) && context.HttpContext.Request.Query.ContainsKey(Key)) { id = context.HttpContext.Request.Query[Key]; } string filePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", area, controllerName + "-" + actionName + (string.IsNullOrEmpty(id) ? "" : ("-" + id)) + .html //ファイルが存在するかどうかを判断します。 if (File.Exists(filePath)) { //存在する場合、直接ファイルを読み取ります。 using (FileStream fs = File.Open(filePath, FileMode.Open)) { using (StreamReader sr = new StreamReader(fs, Encoding.UTF8)) { //contentresultを通じてファイル内容を返します。 ContentResult contentresult = new ContentResult(); contentresult.Content = sr.ReadToEnd(); contentresult.ContentType = "text/html"; context.Result = contentresult; } } }
OnActionExecutedで結果アクションを取得する必要があります。アクション結果のタイプがViewResultであるかどうかを判断し、その場合はコードを実行して結果出力を取得し、上記のルールに従って静的ページを生成します。具体的な実装は以下の通りです。
//結果を取得します。 IActionResult actionResult = context.Result; //結果がViewResultであるかどうかを判断します。 if (actionResult is ViewResult) { ViewResult viewResult = actionResult as ViewResult; //以下のコードはこのViewResultを実行し、結果のhtmlコンテンツをStringBuilerオブジェクトに格納します。 var services = context.HttpContext.RequestServices; var executor = services.GetRequiredService<ViewResultExecutor>(); var option = services.GetRequiredService<IOptions<MvcViewOptions>>(); var result = executor.FindView(context, viewResult); result.EnsureSuccessful(originalLocations: null); var view = result.View; StringBuilder builder = new StringBuilder(); using (var writer = new StringWriter(builder)) { var viewContext = new ViewContext( context, view, viewResult.ViewData, viewResult.TempData, writer, option.Value.HtmlHelperOptions); view.RenderAsync(viewContext).GetAwaiter().GetResult(); //这句一定要调用,否则内容就会是空的 writer.Flush(); } //按照规则生成静态文件名称 string area = context.RouteData.Values["area"].ToString().ToLower(); string controllerName = context.RouteData.Values["controller"].ToString().ToLower(); string actionName = context.RouteData.Values["action"].ToString().ToLower(); string id = context.RouteData.Values.ContainsKey(Key) ? context.RouteData.Values[Key].ToString() : ""; if (string.IsNullOrEmpty(id) && context.HttpContext.Request.Query.ContainsKey(Key)) { id = context.HttpContext.Request.Query[Key]; } string devicedir = Path.Combine(AppContext.BaseDirectory, "wwwroot", area); if (!Directory.Exists(devicedir))}} { Directory.CreateDirectory(devicedir); } //ファイルに書き込みます string filePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", area, controllerName + "-" + actionName + (string.IsNullOrEmpty(id) ? "" : ("-" + id)) + .html using (FileStream fs = File.Open(filePath, FileMode.Create)) { using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) { sw.Write(builder.ToString()); } } //現在の結果を出力します ContentResult contentresult = new ContentResult(); contentresult.Content = builder.ToString(); contentresult.ContentType = "text/html"; context.Result = contentresult; }
前述のキーについて、対応する属性を直接追加します
public string Key { get;set; }
このフィルタを使用できます。使用方法は以下の通りです:コントローラーやコントローラーメソッドに[StaticFileHandlerFilter]属性を追加します。異なるキーを設定したい場合は[StaticFileHandlerFilter(Key="設定値")]を使用します
静的化は既に実装されていますが、更新の事も考慮する必要があります。もしバックエンドが一つの記事を更新したら、静的ページも更新する必要があります。方法はたくさんあります:一つの方法は、バックエンドで内容を更新する際に、対応する静的ページを同期で削除するだけです。ここでは、別の方法を紹介します。定期的な更新です。静的ページには一定の有効期限があります。その有効期限が過ぎたら自動的に更新されます。このロジックを実装するには、OnActionExecutingメソッドで静的ページの作成時間を取得し、現在時刻と比較して有効期限が切れていないかを判断します。有効期限が切れていない場合は内容を直接出力し、有効期限が切れている場合は後続のロジックを実行します。具体的なコードは以下の通りです:
//ファイル情報オブジェクトを取得します FileInfo fileInfo = new FileInfo(filePath); //決算時間間隔、もし2分以内なら直接出力します、もちろんここのルールは変更できます TimeSpan ts = DateTime.Now - fileInfo.CreationTime; if(ts.TotalMinutes<=2) { using (FileStream fs = File.Open(filePath, FileMode.Open)) { using (StreamReader sr = new StreamReader(fs, Encoding.UTF8)) { ContentResult contentresult = new ContentResult(); contentresult.Content = sr.ReadToEnd(); contentresult.ContentType = "text/html"; context.Result = contentresult; } } }
これで静的リダイレクトは完了です。現在の処理方法は、訪問性能を一定程度向上させることはできますが、大規模なポータルシステムには十分ではありません。上記の方法に従って、他の機能の拡張もできます。例えば、静的ページを生成してCDNに公開することもできます。または、別のコンテンツサーバーに公開することもできますなど。どんな方法でも、実現方法は同じです。
これでこの記事は全てです。皆さんの学習に役立つことを願っています。皆さんも呐喊教程をたくさんサポートしてください。
声明:この記事の内容はインターネットから提供されています。著作権は原作者に帰属します。コンテンツはインターネットユーザーにより自発的に貢献し、アップロードされました。このウェブサイトは所有権を持ちません。手動で編集されていません。また、関連する法的責任を負いません。著作権侵害の疑われる内容がある場合は、メールで notice#w までお知らせください。3codebox.com(メールを送信する際には、#を@に変更してください。)で通報し、関連する証拠を提供してください。一旦確認がつき、このサイトは侵害される内容をすぐに削除します。