asp.net core mvc實現偽靜態功能

来源:http://www.cnblogs.com/dxp909/archive/2017/02/17/6410689.html
-Advertisement-
Play Games

在大型網站系統中,為了提高系統訪問性能,往往會把一些不經常變得內容發佈成靜態頁,比如商城的產品詳情頁,新聞詳情頁,這些信息一旦發佈後,變化的頻率不會很高,如果還採用動態輸出的方式進行處理的話,肯定會給伺服器造成很大的資源浪費。但是我們又不能針對這些內容都獨立製作靜態頁,所以我們可以在系統中利用偽靜態 ...


  在大型網站系統中,為了提高系統訪問性能,往往會把一些不經常變得內容發佈成靜態頁,比如商城的產品詳情頁,新聞詳情頁,這些信息一旦發佈後,變化的頻率不會很高,如果還採用動態輸出的方式進行處理的話,肯定會給伺服器造成很大的資源浪費。但是我們又不能針對這些內容都獨立製作靜態頁,所以我們可以在系統中利用偽靜態的方式進行處理,至於什麼是偽靜態,大家可以百度下。我們這裡就來介紹一下,在asp.net core mvc中實現偽靜態的方式。

  mvc框架中,view代表的是視圖,它執行的結果就是最終輸出到客戶端瀏覽器的內容,包含html,css,js等。如果我們想實現靜態化,我們就需要把view執行的結果保存成一個靜態文件,保存到指定的位置上,比如磁碟、分散式緩存等,下次再訪問就可以直接讀取保存的內容,而不用再執行一次業務邏輯。那asp.net core mvc要實現這樣的功能,應該怎麼做?答案是使用過濾器,在mvc框架中,提供了多種過濾器類型,這裡我們要使用的是動作過濾器,動作過濾器提供了兩個時間點:動作執行前,動作執行後。我們可以在動作執行前,先判斷是否已經生成了靜態頁,如果已經生成,直接讀取文件內容輸出即可,後續的邏輯就執行跳過。如果沒有生產,就繼續往下走,在動作執行後這個階段捕獲結果,然後把結果生成的靜態內容進行保存。

  那我們就來具體的實現代碼,首先我們定義一個過濾器類型,我們成為StaticFileHandlerFilterAttribute,這個類派生自框架中提供的ActionFilterAttribute,StaticFileHandlerFilterAttribute重寫基類提供的兩個方法: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;
                }

  上面提到的Key,我們直接增加對應的屬性

        public string Key
        {
            get;set;
        } 

  這樣我們就可以使用這個過濾器了,使用的方法:在控制器或者控制器方法上增加 [StaticFileHandlerFilter]特性,如果想配置不同的Key,可以使用 [StaticFileHandlerFilter(Key="設置的值")]

  靜態化已經實現了,我們還需要考慮更新的事,如果後臺把一篇文章更新了,我們得把靜態頁也更新下,方案有很多:一種是在後臺進行內容更新時,同步把對應的靜態頁刪除即可。我們這裡介紹另外一種,定時更新,就是讓靜態頁有一定的有效期,過了這個有效期自動更新。要實現這個邏輯,我們需要在OnActionExecuting方法中獲取靜態頁的創建時間,然後跟當前時間對比,判斷是否已過期,如果未過期直接輸出內容,如果已過期,繼續執行後面的邏輯。具體代碼如下:

  

//獲取文件信息對象
FileInfo fileInfo=new FileInfo(filePath);
//結算時間間隔,如果小於等於兩分鐘,就直接輸出,當然這裡的規則可以改
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上,也可以發佈到單獨的一個內容伺服器,等等。不管是什麼方式,實現思路都是一樣的。

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我校的課程真是跟不上時代發展,甚至還在教授8051/8052單片機的內容,於是不甘寂寞的我就自己踏入了STM32單片機的坑…… 首先,我現在大二,剛學完模擬電子技術,還沒有學習數字電路技術,於是自學單片機開發會有一定困難,而我校要到大三才能開放單片機課程,這就很有趣了,我不得不去啃一些完全沒見過的玩 ...
  • 等待隊列 是內核中實現進程調度的一個十分重要的數據結構,其任務是維護一個鏈表,鏈表中每一個節點都是一個PCB(進程式控制制塊), 內核會將PCB掛在等待隊列中的所有進程都調度為睡眠狀態,直到某個喚醒的條件發生 。應用層的阻塞IO與非阻塞IO的使用我已經在 "Linux I/O多路復用" 一文中討論過了, ...
  • 簡介 RabbitMQ:一個消息系統,基於 AMQP 系統協議。 優點:健壯、使用簡單、開源和支持各種流行的語言等。 MQ(Message Queue):消息隊列的簡稱,是一種應用程式之間的通信機制。 用途:將無需立即回調獲取返回結果,並且耗時的操作,使用非同步處理的方式提高伺服器的吞吐量及性能。 ...
  • 初步應用vs2012這軟體,語言選擇c# , 框架選擇4(不要選擇最前和太後的框架)然後改個名字和保存路徑點確定就行了。 在main函數中寫代碼,大括弧裡面。 首先是最基本的輸入與輸出: Console.WriteLine(");//直接會輸出引號裡面的內容(如果直接寫入Write的話就不會換行。) ...
  • 電子面單開發流程 服務程式 生成單號,改變三個表: 抓類型表,抓取未處理的充值記錄->根據類型取面單類型表的最大單號(判斷是否在起始和結束之間,並設置郵件或者簡訊預警 )->根據單號規則和充值的數量生成單號明細入單號明細表,改變充值記錄的處理狀態 改變面單類型最大單號(事務提交)。 ...
  • 經過本周的努力,昨晚終於完成OSS.Social微信項目的標準庫支持,當前項目你已經可以同時在.net framework和.net core 中進行調用,調用方法也發生了部分變化,這裡我簡單分享下,主要包含下邊幾個部分: · 移植後的變化 · 和OSS.Common,OSS.Http關係 · 非同步 ...
  • #define aaa //放在代碼最前面 int a = 1; a = a + 1; #if !aaa {a = a + 1;}#elif !aaaaa {a=a+11;}#endif Console.WriteLine(a); Console.ReadKey(); 據說與版本有關 ,#undef ...
  • 為什麼叫T4?因為簡寫為4個T。 T4(Text Template Transformation Toolkit)是微軟官方在VisualStudio 2008中開始使用的代碼生成引擎。在 Visual Studio 中,“T4 文本模板”是由一些文本塊和控制邏輯組成的混合模板,它可以生成文本文件。 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...