ASP.NET Core MVC/WebApi基礎(chǔ)系列1
>前言
最近發(fā)表的EF Core貌似有點(diǎn)多,可別誤以為我只專攻EF Core哦,私下有時(shí)間也是一直在看ASP.NET Core的內(nèi)容,所以后續(xù)會(huì)穿插講EF Core和ASP.NET Core,別認(rèn)為你會(huì)用ASP.NET Core就自認(rèn)為你很了解ASP.NET Core,雖說是基礎(chǔ)系列但也是也有你不知道的ASP.NET Core。
UseStaticFiles、UseDefaultFiles、UseDirectoryBrowser、UseFileServer
當(dāng)我們創(chuàng)建默認(rèn).NET Core Web應(yīng)用程序時(shí),.NET Core默認(rèn)為我們注入了StaticFiles從而可使用wwwroot目錄下的靜態(tài)文件,請(qǐng)注意這里注入StaticFiles是基于wwwroot目錄下的靜態(tài)文件,此時(shí)我們?nèi)缦峦ㄟ^使用UseDefaultFiles啟用默認(rèn)靜態(tài)文件。
app.UseDefaultFiles(); app.UseStaticFiles();
在此之前呢,我們在wwwroot目錄下創(chuàng)建了四個(gè)靜態(tài)HTML文件,如下:


根據(jù)官方文檔說明,我們創(chuàng)建如上四個(gè)靜態(tài)html,同時(shí)也會(huì)根據(jù)如上順序在wwwroot目錄下查找靜態(tài)html,查找到了default.htm,所以此時(shí)如上顯示對(duì)應(yīng)內(nèi)容,若我們刪除第一個(gè)html,則會(huì)查找default.html,以此類推。要是我們將注入順序顛倒會(huì)這樣呢?如下:
app.UseStaticFiles(); app.UseDefaultFiles();

此時(shí)會(huì)出現(xiàn)頁面404找不到頁面,這是為何呢?官方文檔強(qiáng)調(diào)必須將注入默認(rèn)文件放在注入靜態(tài)文件前面,主要是因?yàn)樽⑷肽J(rèn)文件只是進(jìn)行URL重寫,告訴路由我要到wwwroot目錄下查找靜態(tài)文件,但是實(shí)際上提供靜態(tài)文件的是StaticFiles,所以這也是為什么必須將注入默認(rèn)文件放在注入靜態(tài)文件前面。但是如果我們非要將注入默認(rèn)文件放在注入靜態(tài)文件前面,我們該如何做呢?接下來通過使用UseFileServer,UseFileServer是UseDefaultFiles和UseStaticFiles的組合體,既然是組合體,我們將UseFileServer放在第一位不就這個(gè)問題了嗎,我們來試試,如下:
app.UseFileServer(); app.UseStaticFiles(); app.UseDefaultFiles();
結(jié)果將會(huì)呈現(xiàn)默認(rèn)靜態(tài)html,這里我就不再演示了,有興趣的童鞋可自行研究。接下來我們再來看看啟用目錄瀏覽,啟用目錄瀏覽和我們在IIS上啟用目錄瀏覽一樣,如下:
app.UseDirectoryBrowser(); app.UseFileServer(); app.UseStaticFiles(); app.UseDefaultFiles();

這里就不用我再多說,那么問題來了:要是我們將啟用目錄瀏覽放到使用MVC路由后面會(huì)怎樣呢?此時(shí)啟用目錄瀏覽會(huì)覆蓋MVC路由?不會(huì),可自行驗(yàn)證。
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseDirectoryBrowser();
自定義默認(rèn)文件目錄
關(guān)于修改默認(rèn)文件名稱等基礎(chǔ),官方文檔有詳細(xì)說明,這里就不再演示,浪費(fèi)篇幅,接下來我們來重點(diǎn)講解不一樣的。比如默認(rèn)啟用靜態(tài)文件,是放在wwwroot根目錄下,要是我們想將靜態(tài)文件放在所給第一張圖中dist文件夾下呢?此時(shí)我們應(yīng)該如何做呢?因?yàn)?NET Core默認(rèn)將wwwroot目錄作為靜態(tài)文件目錄,所以此時(shí)我們需要改變其目錄到wwwroot目錄下的dist目錄,通過使用UseWebRoot方法,將Web靜態(tài)目錄更改到wwwroot下的dist目錄,當(dāng)然同時(shí)啟用默認(rèn)文件(UseDefaultFiles)如下:
.UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "dist"))


那么問題又來了,此時(shí)假設(shè)我想將默認(rèn)靜態(tài)文件放在外部即項(xiàng)目根目錄,此時(shí)我們應(yīng)該如何做呢?比如訪問如下靜態(tài)html文件。

此時(shí)我們可利用UseDefaultFiles方法的重載,將目錄更換到項(xiàng)目根目錄下的OutDefaultHtml目錄,如下:
var fileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath,
"OutDefaultHtml"));
app.UseDefaultFiles(new DefaultFilesOptions()
{
FileProvider = fileProvider,
DefaultFileNames = new [] { "OutDefault.html" }
});
因?yàn)槲覀兏鼡Q了查找靜態(tài)html的目錄,同時(shí)最終提供默認(rèn)文件的是UseStaticFiles,所以我們也需要通過UseStaticFiles方法的重載切換目錄不再是wwwroot,如下:
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = fileProvider
});

除了上述通過聯(lián)合使用UseDefaultFiles和UseStaticFiles之外,是否還有更簡潔的方式呢?當(dāng)然是有的,當(dāng)默認(rèn)靜態(tài)文件放在wwwroot目錄下不再滿足我們的需求時(shí),我們需要自定義默認(rèn)靜態(tài)文件所放置目錄時(shí),推薦使用二者的聯(lián)合體即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詳解
在大部分情況下,我們都將靜態(tài)文件放在wwwroot目錄下,但是有那么百分之十的情況下會(huì)將靜態(tài)文件放在項(xiàng)目根目錄,那么此時(shí)使用默認(rèn)注入的UseStaticFiles方法就不再適用,此時(shí)我們需要用到其重載方法。比如我們要訪問如下圖中的mvc_course.gif,我們該如何做呢?

上面已經(jīng)講過,需要使用UseStaticFiles方法的重載,第一個(gè)參數(shù)將目錄切換到靜態(tài)文件所在目錄,第二個(gè)參數(shù)是虛擬路徑用來訪問靜態(tài)文件,為了不對(duì)外暴露實(shí)際物理路徑,如下:
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")
//});


該重載方法還有一個(gè)委托參數(shù)OnPrepareResponse,這個(gè)主要用來緩存靜態(tài)文件,接下來我們來重點(diǎn)講講,其實(shí)本文都是重點(diǎn),哈哈,簡單的大家直接去看官網(wǎng)吧。
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;
}
});
在官方文檔上是進(jìn)行如上設(shè)置,但實(shí)際上官方文檔APi已經(jīng)過時(shí),對(duì)于請(qǐng)求頭的設(shè)置直接有HeaderNames這樣一個(gè)枚舉來進(jìn)行設(shè)置,不再通過字符串的形式來設(shè)置,這樣不容易出錯(cuò)且方便,上述對(duì)于請(qǐng)求頭中緩存控制的設(shè)置有如下兩種方式皆可。

在響應(yīng)頭中添加緩存控制有什么實(shí)際作用?此時(shí)就要談到緩存控制的原理了。上述緩存控制設(shè)置的過期時(shí)間為60秒。當(dāng)?shù)谝淮握?qǐng)求時(shí)返回200,在此間隙即60秒內(nèi)反復(fù)刷新都會(huì)是200,同時(shí)從瀏覽器緩存中讀取,一旦過了60秒,再刷新此時(shí)會(huì)再去讀取服務(wù)器上的圖片,發(fā)現(xiàn)圖片未發(fā)生改變返回304未修改。那么問題來了,如果我們在此間隙內(nèi)修改了圖片的內(nèi)容,然后再刷新圖片的內(nèi)容是否會(huì)發(fā)生改變呢?答案是:不會(huì),只要在緩存間隙時(shí)間內(nèi),即使我們修改了圖片的內(nèi)容,再刷新還是顯示原來的圖片(除非進(jìn)行ctrl+F5強(qiáng)制刷新才行)。好了講了這么多,我們繼續(xù)拓展一下,再來看看ASP.NET Core中TagHelper特性:asp-append-version特性。該特性和緩存控制原理是一樣的么,接下來我們來談?wù)刟sp-append-version以及其原理。
asp-append-version詳解及其原理
我們以wwwroot目錄下images文件下的圖片為例,然后在頁面上訪問圖片加上asp-append-version看看,如下:
<img src="~/images/mvc_course.gif" asp-append-version="true" />


此時(shí)響應(yīng)返回鏈接地址為:http://localhost:63277/images/mvc_course.gif?v=y3F-lvD7XoqGqLIWq_WsuFN9POPSjit1Au6_0iRrgwE,我們從如上圖也可看到,此時(shí)在圖片后面類似加了一個(gè)版本號(hào)v,我們反復(fù)刷新版本號(hào)后面的字符串一直未變,那么這個(gè)類似于哈希碼的值是怎么得來的呢?基于請(qǐng)求URL和圖片內(nèi)容計(jì)算出哈希碼即版本號(hào)。也就說只要我們更改了圖片的內(nèi)容,當(dāng)刷新或者再次訪問此頁面時(shí)內(nèi)容相應(yīng)會(huì)進(jìn)行對(duì)應(yīng)更新,這也就是我們所說的緩存擊穿,相對(duì)于緩存控制而言,只要在緩存間隙時(shí)間內(nèi)修改了圖片內(nèi)容,除非進(jìn)行強(qiáng)制刷新,否則圖片依然顯示舊的圖片,而asp-append-version特性則是你變,我變,你不變,我一成不變。是不是就這么簡單呢?接下來我們訪問一下項(xiàng)目根目錄下的圖片看看,通過UseStaticFiles重載訪問外部圖片,同時(shí)加上asp-append-version特性。
<img src="/outfiles/mvc_course.gif" asp-append-version="true" />

WOW,看到了什么沒有,發(fā)現(xiàn)了什么沒有,至此我們可以得出結(jié)論:asp-append-version特性實(shí)現(xiàn)圖片緩存只是針對(duì)于WebRoot目錄下的靜態(tài)文件,而外部靜態(tài)文件則無效。
那么既然問題已經(jīng)很凸出了,asp-append-version主要是針對(duì)于WebRoot目錄下的靜態(tài)文件,而WebRoot里面只有wwwroot,所以我們可以稱之為只對(duì)wwwroot目錄下的靜態(tài)文件才生效,所以我們是否可以嘗試將外部文件目錄也置于WebRoot目錄呢?從而實(shí)現(xiàn)對(duì)外部靜態(tài)文件的緩存呢?我們下面來做嘗試,在Startup.cs中默認(rèn)注入U(xiǎn)seStaticFiles,我們擱置不變,這樣對(duì)默認(rèn)針對(duì)wwwroot下的樣式、腳本、文件都不會(huì)發(fā)生任何改變,我們只是再來注入一個(gè)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"
});
如上針對(duì)默認(rèn)的WebRoot即wwwroot保持不變,我們在此基礎(chǔ)上添加外部目錄從而作為復(fù)合FileProvider作為WebRoot,這樣一切都未變。我們再來進(jìn)行如下訪問。
<img src="/mvc_course.gif" asp-append-version="true" />
如上是針對(duì)OutStaticFiles作為WebRoot目錄訪問其靜態(tài)文件,斷不可加上outfiles虛擬路徑,這樣就當(dāng)做是外部靜態(tài)文件,從而不會(huì)有版本號(hào)出現(xiàn),結(jié)果如下:

我們?nèi)绾巫远x實(shí)現(xiàn)對(duì)外部文件也添加類似于asp-append-version特性版本號(hào)的效果呢? 上述我們已經(jīng)明確講解到asp-append-version本質(zhì)原理則是基于請(qǐng)求URL和請(qǐng)求圖片內(nèi)容來計(jì)算版本號(hào)從而實(shí)現(xiàn)緩存,關(guān)于緩存我們大可借助IMemoryCache接口來進(jìn)行緩存,請(qǐng)求的路徑我們可以通過請(qǐng)求上下文獲取到,同時(shí)也可通過環(huán)境變量拿到請(qǐng)求靜態(tài)文件所在目錄,所以接下來我們只需要實(shí)現(xiàn)視圖的擴(kuò)展方法即可。
視圖擴(kuò)展方法通過指向IRazorPage接口,然后參數(shù)則是我們的文件路徑,ASP.NET Core有了依賴注入讓我們甚為歡喜,我們通過視圖中的視圖上下文拿到請(qǐng)求上下文。然后拿到已經(jīng)注入的IMemoryCache和IHostingEnviroment接口,關(guān)于文件版本號(hào),ASP.NET Core給我們提供了FileVersionProvider類,如下:

我們將參數(shù)傳遞到FileVersionProvider構(gòu)造函數(shù)中去,最后將得到的文件版本號(hào)添加到我們請(qǐng)求的文件路徑尾巴上,代碼如下:
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);
}
}
我們利用上述自定義實(shí)現(xiàn)的Razor視圖擴(kuò)展方法來訪問圖片從而得到版本號(hào)試試,如下:
<img src="@this.AddAppendVersion("/mvc_course.gif")"

總結(jié)
本文詳細(xì)講解了ASP.NET Core MVC中靜態(tài)文件以及緩存控制、asp-append-version本質(zhì)原理,同時(shí)講解了緩存控制和asp-append-version區(qū)別所在。默認(rèn)情況下,asp-append-version只針對(duì)wwwroot有效,因?yàn)樵赪ebRoot里面只存在wwwroot,要想對(duì)外部文件有效,可將外部文件所在目錄也作為WebRoot來使用。
- ASP.Net Core MVC基礎(chǔ)系列之中間件
- ASP.Net Core MVC基礎(chǔ)系列之服務(wù)注冊和管道
- ASP.Net?Core?MVC基礎(chǔ)系列之獲取配置信息
- ASP.Net?Core?MVC基礎(chǔ)系列之項(xiàng)目創(chuàng)建
- asp.net mvc core管道及攔截器的理解
- ASP.NET Core MVC學(xué)習(xí)之視圖組件(View Component)
- ASP.NET Core MVC基礎(chǔ)學(xué)習(xí)之局部視圖(Partial Views)
- ASP.NET Core MVC學(xué)習(xí)教程之路由(Routing)
- ASP.NET Core MVC/WebApi基礎(chǔ)系列2
- ASP.Net?Core?MVC基礎(chǔ)系列之環(huán)境設(shè)置
相關(guān)文章
ASP.NET堆和棧三之引用類型對(duì)象拷貝和內(nèi)存分配
這篇文章介紹了ASP.NET堆和棧中引用類型對(duì)象的拷貝和內(nèi)存分配,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08
如何在.NET Core應(yīng)用中使用NHibernate詳解
NHibernate 是一個(gè)基于.Net 的針對(duì)關(guān)系型數(shù)據(jù)庫的對(duì)象持久化類庫。下面這篇文章主要給大家介紹了關(guān)于如何在.NET Core應(yīng)用中使用NHibernate的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-08-08
.net core webapi通過中間件獲取請(qǐng)求和響應(yīng)內(nèi)容的方法
這篇文章主要介紹了.net core webapi通過中間件獲取請(qǐng)求和響應(yīng)內(nèi)容的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09
.Net中關(guān)于stirng轉(zhuǎn)System.Type的一種實(shí)現(xiàn)思路詳解
這篇文章主要給大家介紹了.Net中關(guān)于stirng轉(zhuǎn)System.Type的一種實(shí)現(xiàn)思路的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
Asp.Net使用Npoi導(dǎo)入導(dǎo)出Excel的方法
這篇文章主要介紹了Asp.Net使用Npoi導(dǎo)入導(dǎo)出Excel的方法,采用該方法在導(dǎo)出Excel的時(shí)候不需要office組件的支持,而在導(dǎo)入Excel的時(shí)候采用OleDb的方式,需要office組件的支持。是一個(gè)非常實(shí)用的技巧,需要的朋友可以參考下2014-09-09
asp.net網(wǎng)站開發(fā)包wq.dll打包下載
這個(gè)wq.dll主要是用來給Web群和C#聯(lián)盟群及GUI群的朋友使用的,其它群和使用控件開發(fā)web的朋友可以直接無視,這個(gè)封裝好的包是一個(gè)基礎(chǔ)開發(fā)包,可以輕松的幫你完成一些小型網(wǎng)站的開發(fā),支持.Net Framework2.0(及以上平臺(tái))。2009-10-10
ASP.NET環(huán)境下為網(wǎng)站增加IP過濾功能
通過深入的交流和溝通,確認(rèn)了該發(fā)電廠在企業(yè)網(wǎng)站用戶訪問控制方面的改進(jìn)要求2009-06-06
ASP.NET MVC 導(dǎo)出Word報(bào)表
本文主要介紹了ASP.NET MVC 導(dǎo)出Word報(bào)表的方法,具有很好的參考價(jià)值,下面跟著小編一起來看下吧2017-02-02

