asp.net core mvc实现伪静态功能
在大型網站系統中,為了提高系統訪問性能,往往會把一些不經常變得內容發布成靜態頁,比如商城的產品詳情頁,新聞詳情頁,這些信息一旦發布后,變化的頻率不會很高,如果還采用動態輸出的方式進行處理的話,肯定會給服務器造成很大的資源浪費。但是我們又不能針對這些內容都獨立制作靜態頁,所以我們可以在系統中利用偽靜態的方式進行處理,至于什么是偽靜態,大家可以百度下。我們這里就來介紹一下,在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上,也可以發布到單獨的一個內容服務器,等等。不管是什么方式,實現思路都是一樣的。
總結
以上是生活随笔為你收集整理的asp.net core mvc实现伪静态功能的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 个性化配置你的SQL Server on
- 下一篇: 分布式事务与一致性算法Paxos amp
