你所不知道的ASP.NET Core MVC/WebApi基礎系列 (一)

来源:https://www.cnblogs.com/cxt618/archive/2019/04/05/10660234.html
-Advertisement-
Play Games

轉自博客:https://www.cnblogs.com/CreateMyself/p/9235968.html 前言 最近發表的EF Core貌似有點多,可別誤以為我只專攻EF Core哦,私下有時間也是一直在看ASP.NET Core的內容,所以後續會穿插講EF Core和ASP.NET Cor ...


轉自博客:https://www.cnblogs.com/CreateMyself/p/9235968.html

前言

最近發表的EF Core貌似有點多,可別誤以為我只專攻EF Core哦,私下有時間也是一直在看ASP.NET Core的內容,所以後續會穿插講EF Core和ASP.NET Core,別認為你會用ASP.NET Core就自認為你很瞭解ASP.NET Core,雖說是基礎系列但也是也有你不知道的ASP.NET Core。

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看看,如下:

img src="~/images/mvc_course.gif" asp-append-version="true" />

 

  此時響應返回鏈接地址為:http://localhost:63277/images/mvc_course.gif?v=y3F-lvD7XoqGqLIWq_WsuFN9POPSjit1Au6_0iRrgwE,我們從如上圖也可看到,此時在圖片後面類似加了一個版本號v,我們反覆刷新版本號後面的字元串一直未變,那麼這個類似於哈希碼的值是怎麼得來的呢?基於請求URL和圖片內容計算出哈希碼即版本號。也就說只要我們更改了圖片的內容,當刷新或者再次訪問此頁面時內容相應會進行對應更新,這也就是我們所說的緩存擊穿,相對於緩存控制而言,只要在緩存間隙時間內修改了圖片內容,除非進行強制刷新,否則圖片依然顯示舊的圖片,而asp-append-version特性則是你變,我變,你不變,我一成不變。是不是就這麼簡單呢?接下來我們訪問一下項目根目錄下的圖片看看,通過UseStaticFiles重載訪問外部圖片,同時加上asp-append-version特性。 

<img src="/outfiles/mvc_course.gif" asp-append-version="true" />

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,這樣一切都未變。我們再來進行如下訪問。 

<img src="/mvc_course.gif" asp-append-version="true" />

如上是針對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視圖擴展方法來訪問圖片從而得到版本號試試,如下: 

<img src="@this.AddAppendVersion("/mvc_course.gif")" 

總結

本文詳細講解了ASP.NET Core MVC中靜態文件以及緩存控制、asp-append-version本質原理,同時講解了緩存控制和asp-append-version區別所在。預設情況下,asp-append-version只針對wwwroot有效,因為在WebRoot裡面只存在wwwroot,要想對外部文件有效,可將外部文件所在目錄也作為WebRoot來使用。


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

-Advertisement-
Play Games
更多相關文章
  • 一,本機配置 Win10 64bit NVIDIA GeForce GTX 960M Python3.7(Anaconda) 二,安裝CUDA 親測,TensorFlow-gpu1.13.1支持cuda10.0的版本,所以我們可直接選擇cuda10.0的版本 Window10下載CUDA10 安裝步 ...
  • PHP原生寫的生成圖片縮略圖類,本文以京東商品圖片為例,分別生成三種不同尺寸的圖片。調用方法很簡單隻要傳參數高度和寬度,及新圖片的名稱。 引入縮略圖類 生成三個不同尺寸縮略圖 本實例下載:https://www.sucaihuo.com/php/867.html ...
  • John and Mary want to travel between a few towns A, B, C ... Mary has on a sheet of paper a list of distances between these towns. ls = [50, 55, 57, 5 ...
  • 1.變數的作用域和靜態變數 函數的參數以及參數的引用傳遞 函數的返回值以及引用返回 外部文件的導入 系統內置函數的考察 變數的作用域也稱為變數的範圍,變數的範圍即他定義上下文的背景(也是它生效的範圍)。大部分php變數只有一生效的範圍,這個單獨的範圍也包括include 和require 引入的文件 ...
  • 矩陣快速冪 設答案為f(i) 舉個例子: 當i==2時,包含0的值有:10,20,30,40,50,60,70,80,90,100;0的個數為11,f(2)=11; i==3時;可以從i==2的情況遞推, 第一步:i==2時的數據範圍:1-100;在這100個數後面補0;補0後,這些數在1-1000 ...
  • 一、可變與不可變類型 二、數字類型 1、用途 2、定義方式 3、總結 三、字元串類型 1、作用 2、定義方式 3、常用操作+內置方法 # 優先掌握的操作: 4、總結 四、列表類型 1、用途 2、定義方式 3、常用操作+內置的方法 4、總結 ...
  • libmxml是一個開源、小巧的C語言xml庫。這裡簡單分析一下它是用什麼樣的數據結構來保存分析過的xml文檔。 mxml關鍵的結構體mxml_node_t是這樣的實現的: 它使用左孩子右兄弟的樹形結構來描述xml報文:即下層節點登記在child鏈表,兄弟節點登記在next鏈表。 如果某個節點下麵有 ...
  • 格式:\033[顯示方式;前景色;背景色m + 結尾部分:\033[0m ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...