05_HUD_Qt_for_Python開發之路3
977
2025-04-01
UseStaticFiles、UseDefaultFiles、UseDirectoryBrowser、UseFileServer
當我們創建默認.NET Core Web應用程序時,.NET Core默認為我們注入了StaticFiles從而可使用wwwroot目錄下的靜態文件,請注意這里注入StaticFiles是基于wwwroot目錄下的靜態文件,此時我們如下通過使用UseDefaultFiles啟用默認靜態文件。
app.UseDefaultFiles(); ?app.UseStaticFiles();
在此之前呢,我們在wwwroot目錄下創建了四個靜態HTML文件,如下:
根據官方文檔說明,我們創建如上四個靜態html,同時也會根據如上順序在wwwroot目錄下查找靜態html,查找到了default.htm,所以此時如上顯示對應內容,若我們刪除第一個html,則會查找default.html,以此類推。要是我們將注入順序顛倒會這樣呢?如下:
app.UseStaticFiles(); ????????????app.UseDefaultFiles();
此時會出現頁面404找不到頁面,這是為何呢?官方文檔強調必須將注入默認文件放在注入靜態文件前面,主要是因為注入默認文件只是進行URL重寫,告訴路由我要到wwwroot目錄下查找靜態文件,但是實際上提供靜態文件的是StaticFiles,所以這也是為什么必須將注入默認文件放在注入靜態文件前面。但是如果我們非要將注入默認文件放在注入靜態文件前面,我們該如何做呢?接下來通過使用UseFileServer,UseFileServer是UseDefaultFiles和UseStaticFiles的組合體,既然是組合體,我們將UseFileServer放在第一位不就這個問題了嗎,我們來試試,如下:
app.UseFileServer(); ????????????app.UseStaticFiles(); ????????????app.UseDefaultFiles();
結果將會呈現默認靜態html,這里我就不再演示了,有興趣的童鞋可自行研究。接下來我們再來看看啟用目錄瀏覽,啟用目錄瀏覽和我們在IIS上啟用目錄瀏覽一樣,如下:
app.UseDirectoryBrowser(); ????????????app.UseFileServer(); ????????????app.UseStaticFiles(); ????????????app.UseDefaultFiles();
這里就不用我再多說,那么問題來了:要是我們將啟用目錄瀏覽放到使用MVC路由后面會怎樣呢?此時啟用目錄瀏覽會覆蓋MVC路由?不會,可自行驗證。
app.UseMvc(routes?=> ????????????{ ????????????????routes.MapRoute( ????????????????????name:?"default", ????????????????????template:?"{controller=Home}/{action=Index}/{id?}"); ????????????}); ?????????? ????????????app.UseDirectoryBrowser();
自定義默認文件目錄
關于修改默認文件名稱等基礎,官方文檔有詳細說明,這里就不再演示,浪費篇幅,接下來我們來重點講解不一樣的。比如默認啟用靜態文件,是放在wwwroot根目錄下,要是我們想將靜態文件放在所給第一張圖中dist文件夾下呢?此時我們應該如何做呢?因為.NET Core默認將wwwroot目錄作為靜態文件目錄,所以此時我們需要改變其目錄到wwwroot目錄下的dist目錄,通過使用UseWebRoot方法,將Web靜態目錄更改到wwwroot下的dist目錄,當然同時啟用默認文件(UseDefaultFiles)如下:
.UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(),?"wwwroot",?"dist"))
那么問題又來了,此時假設我想將默認靜態文件放在外部即項目根目錄,此時我們應該如何做呢?比如訪問如下靜態html文件。
此時我們可利用UseDefaultFiles方法的重載,將目錄更換到項目根目錄下的OutDefaultHtml目錄,如下:
var?fileProvider?=?new?PhysicalFileProvider(Path.Combine(env.ContentRootPath,? ???????????????"OutDefaultHtml")); ????????????app.UseDefaultFiles(new?DefaultFilesOptions() ????????????{ ????????????????FileProvider?=?fileProvider, ????????????????DefaultFileNames?=?new?[]?{?"OutDefault.html"?} ????????????});
因為我們更換了查找靜態html的目錄,同時最終提供默認文件的是UseStaticFiles,所以我們也需要通過UseStaticFiles方法的重載切換目錄不再是wwwroot,如下:
app.UseStaticFiles(new?StaticFileOptions() ????????????{ ????????????????FileProvider?=?fileProvider ????????????});
除了上述通過聯合使用UseDefaultFiles和UseStaticFiles之外,是否還有更簡潔的方式呢?當然是有的,當默認靜態文件放在wwwroot目錄下不再滿足我們的需求時,我們需要自定義默認靜態文件所放置目錄時,推薦使用二者的聯合體即UseFileServer。上述我們可修改成如下:
var?fileProvider?=?new?PhysicalFileProvider(Path.Combine(env.ContentRootPath,"OutDefaultHtml"));????????????var?fileServerOptions?=?new?FileServerOptions(); ????????????fileServerOptions.DefaultFilesOptions.DefaultFileNames?=?new[]?{?"OutDefault.html"}; ????????????fileServerOptions.FileProvider?=?fileProvider; ????????????app.UseFileServer(fileServerOptions);
UseStaticFiles詳解
在大部分情況下,我們都將靜態文件放在wwwroot目錄下,但是有那么百分之十的情況下會將靜態文件放在項目根目錄,那么此時使用默認注入的UseStaticFiles方法就不再適用,此時我們需要用到其重載方法。比如我們要訪問如下圖中的mvc_course.gif,我們該如何做呢?
上面已經講過,需要使用UseStaticFiles方法的重載,第一個參數將目錄切換到靜態文件所在目錄,第二個參數是虛擬路徑用來訪問靜態文件,為了不對外暴露實際物理路徑,如下:
app.UseStaticFiles(new?StaticFileOptions() ????????????{ ????????????????FileProvider?=?new?PhysicalFileProvider(Path.Combine(env.ContentRootPath,?"OutStaticFiles")), ????????????????RequestPath?=?"/outfiles" ????????????});????????????????? ????????????或者 ????????????//app.UseStaticFiles(new?StaticFileOptions()????????????//{????????????//????FileProvider?=?new?PhysicalFileProvider(Path.Combine(env.ContentRootPath,?"OutStaticFiles")),????????????//????RequestPath?=?new?PathString("/outfiles")????????????//});
該重載方法還有一個委托參數OnPrepareResponse,這個主要用來緩存靜態文件,接下來我們來重點講講,其實本文都是重點,哈哈,簡單的大家直接去看官網吧。
app.UseStaticFiles(new?StaticFileOptions() ????????????{ ????????????????FileProvider?=?new?PhysicalFileProvider(Path.Combine(env.ContentRootPath,?"OutStaticFiles")), ????????????????RequestPath?=?"/outfiles", ????????????????OnPrepareResponse?=?ctx?=>? ????????????????{????????????????????const?int?cacheControll?=?60; ????????????????????ctx.Context.Response.Headers["Cache-Control"]?=?"public,max-age="?+?cacheControll; ????????????????} ????????????});
在官方文檔上是進行如上設置,但實際上官方文檔APi已經過時,對于請求頭的設置直接有HeaderNames這樣一個枚舉來進行設置,不再通過字符串的形式來設置,這樣不容易出錯且方便,上述對于請求頭中緩存控制的設置有如下兩種方式皆可。
app.UseStaticFiles(new?StaticFileOptions() ????????????{ ????????????????FileProvider?=?new?PhysicalFileProvider(Path.Combine(env.ContentRootPath,?"OutStaticFiles")), ????????????????RequestPath?=?"/outfiles", ????????????????OnPrepareResponse?=?ctx?=>? ????????????????{????????????????????const?int?cacheControll?=?60; ????????????????????ctx.Context.Response.Headers[HeaderNames.CacheControl]?=?"public,max-age="?+?cacheControll; ????????????????} ????????????}); ???????????或者 ????????????app.UseStaticFiles(new?StaticFileOptions() ????????????{ ????????????????FileProvider?=?new?PhysicalFileProvider(Path.Combine(env.ContentRootPath,?"OutStaticFiles")), ????????????????RequestPath?=?"/outfiles", ????????????????OnPrepareResponse?=?ctx?=> ????????????????{????????????????????const?int?cacheControll?=?60;????????????????????var?headers?=?ctx.Context.Response.GetTypedHeaders(); ????????????????????headers.CacheControl?=?new?CacheControlHeaderValue() ????????????????????{ ????????????????????????MaxAge?=?TimeSpan.FromSeconds(cacheControll) ????????????????????};?? ????????????????} ????????????});
在響應頭中添加緩存控制有什么實際作用?此時就要談到緩存控制的原理了。上述緩存控制設置的過期時間為60秒。當第一次請求時返回200,在此間隙即60秒內反復刷新都會是200,同時從瀏覽器緩存中讀取,一旦過了60秒,再刷新此時會再去讀取服務器上的圖片,發現圖片未發生改變返回304未修改。那么問題來了,如果我們在此間隙內修改了圖片的內容,然后再刷新圖片的內容是否會發生改變呢?答案是:不會,只要在緩存間隙時間內,即使我們修改了圖片的內容,再刷新還是顯示原來的圖片(除非進行ctrl+F5強制刷新才行)。好了講了這么多,我們繼續拓展一下,再來看看ASP.NET Core中TagHelper特性:asp-append-version特性。該特性和緩存控制原理是一樣的么,接下來我們來談談asp-append-version以及其原理。
asp-append-version詳解及其原理
我們以wwwroot目錄下images文件下的圖片為例,然后在頁面訪問圖片加上asp-append-version看看,如下:
此時響應返回鏈接地址為:http://localhost:63277/imageshttp://m.bai1xia.com/news/zb_users/upload/2022/05/20220530071049_58052.gif?v=y3F-lvD7XoqGqLIWq_WsuFN9POPSjit1Au6_0iRrgwE,我們從如上圖也可看到,此時在圖片后面類似加了一個版本號v,我們反復刷新版本號后面的字符串一直未變,那么這個類似于哈希碼的值是怎么得來的呢?基于請求URL和圖片內容計算出哈希碼即版本號。也就說只要我們更改了圖片的內容,當刷新或者再次訪問此頁面時內容相應會進行對應更新,這也就是我們所說的緩存擊穿,相對于緩存控制而言,只要在緩存間隙時間內修改了圖片內容,除非進行強制刷新,否則圖片依然顯示舊的圖片,而asp-append-version特性則是你變,我變,你不變,我一成不變。是不是就這么簡單呢?接下來我們訪問一下項目根目錄下的圖片看看,通過UseStaticFiles重載訪問外部圖片,同時加上asp-append-version特性。
WOW,看到了什么沒有,發現了什么沒有,至此我們可以得出結論:asp-append-version特性實現圖片緩存只是針對于WebRoot目錄下的靜態文件,而外部靜態文件則無效。
那么既然問題已經很凸出了,asp-append-version主要是針對于WebRoot目錄下的靜態文件,而WebRoot里面只有wwwroot,所以我們可以稱之為只對wwwroot目錄下的靜態文件才生效,所以我們是否可以嘗試將外部文件目錄也置于WebRoot目錄呢?從而實現對外部靜態文件的緩存呢?我們下面來做嘗試,在Startup.cs中默認注入UseStaticFiles,我們擱置不變,這樣對默認針對wwwroot下的樣式、腳本、文件都不會發生任何改變,我們只是再來注入一個UseStaticFiles而已,如下:
var?compositeProvider?=?new?CompositeFileProvider ????????????( ????????????????env.WebRootFileProvider,????????????????new?PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(),?"OutStaticFiles")) ????????????); ????????????env.WebRootFileProvider?=?compositeProvider; ????????????app.UseStaticFiles(new?StaticFileOptions() ????????????{ ????????????????FileProvider?=?compositeProvider, ????????????????RequestPath?=?"/outfiles" ????????????});
如上針對默認的WebRoot即wwwroot保持不變,我們在此基礎上添加外部目錄從而作為復合FileProvider作為WebRoot,這樣一切都未變。我們再來進行如下訪問。
如上是針對OutStaticFiles作為WebRoot目錄訪問其靜態文件,斷不可加上outfiles虛擬路徑,這樣就當做是外部靜態文件,從而不會有版本號出現,結果如下:
我們如何自定義實現對外部文件也添加類似于asp-append-version特性版本號的效果呢? 上述我們已經明確講解到asp-append-version本質原理則是基于請求URL和請求圖片內容來計算版本號從而實現緩存,關于緩存我們大可借助IMemoryCache接口來進行緩存,請求的路徑我們可以通過請求上下文獲取到,同時也可通過環境變量拿到請求靜態文件所在目錄,所以接下來我們只需要實現視圖的擴展方法即可。
視圖擴展方法通過指向IRazorPage接口,然后參數則是我們的文件路徑,ASP.NET Core有了依賴注入讓我們甚為歡喜,我們通過視圖中的視圖上下文拿到請求上下文。然后拿到已經注入的IMemoryCache和IHostingEnviroment接口,關于文件版本號,ASP.NET Core給我們提供了FileVersionProvider類,如下:
我們將參數傳遞到FileVersionProvider構造函數中去,最后將得到的文件版本號添加到我們請求的文件路徑尾巴上,代碼如下:
public?static?class?RazorPageExtension ????{????????public?static?string?AddAppendVersion(this?IRazorPage?page,?string?path) ????????{????????????var?context?=?page.ViewContext.HttpContext;????????????var?memoryCache?=?context.RequestServices.GetService(typeof(IMemoryCache))?as?IMemoryCache;????????????var?hostingEnviroment?=?context.RequestServices.GetService(typeof(IHostingEnvironment))?as?IHostingEnvironment;????????????var?fileversionProvider?=?new?FileVersionProvider(hostingEnviroment.WebRootFileProvider,?memoryCache,?context.Request.Path);????????????return?fileversionProvider.AddFileVersionToPath(path); ????????} ????}
我們利用上述自定義實現的Razor視圖擴展方法來訪問圖片從而得到版本號試試,如下:
總結
本文詳細講解了ASP.NET Core MVC中靜態文件以及緩存控制、asp-append-version本質原理,同時講解了緩存控制和asp-append-version區別所在。默認情況下,asp-append-version只針對wwwroot有效,因為在WebRoot里面只存在wwwroot,要想對外部文件有效,可將外部文件所在目錄也作為WebRoot來使用。
MVC ASP.NET HTML
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。