1. 程式人生 > >ASP.NET Core應用針對靜態檔案請求的處理[5]: DefaultFilesMiddleware中介軟體如何顯示預設頁面

ASP.NET Core應用針對靜態檔案請求的處理[5]: DefaultFilesMiddleware中介軟體如何顯示預設頁面

DefaultFilesMiddleware中介軟體的目的在於將目標目錄下的預設檔案作為響應內容。我們知道,如果直接請求的就是這個預設檔案,那麼前面介紹的StaticFileMiddleware中介軟體會將這個檔案響應給客戶端。如果我們能夠將針對目錄的請求重定向到這個預設檔案上,一切就迎刃而解了。實際上DefaultFilesMiddleware中介軟體的實現邏輯很簡單,它採用URL重寫的形式修改了當前請求的地址,即將針對目錄的URL修改成針對預設檔案的URL。[本文已經同步到《ASP.NET Core框架揭祕》之中]

我們照例先來看看DefaultFilesMiddleware型別的定義。和其他兩個中介軟體類似,DefaultFilesMiddleware的構造就有一個IOptions<DefaultFilesOptions>型別的引數來指定相關的配置選項。由於DefaultFilesMiddleware中介軟體本質上依然體現了請求路徑與某個物理目錄的對映,所以DefaultFilesOptions依然派生於SharedOptionsBase。DefaultFilesOptions的DefaultNames屬性包含了預定義的預設檔名,我們可以看到它預設包含四個名稱(default.htm、default.html、index.htm或者index.html)。

   1: public class DefaultFilesMiddleware
   2: {
   3:     public DefaultFilesMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, IOptions<DefaultFilesOptions> options);
   4:     public Task Invoke(HttpContext context);
   5: }
   6:  
   7: public class DefaultFilesOptions : SharedOptionsBase
   8: {
   9:     public IList<string> DefaultFileNames { get; set; }
  10:  
  11:     public DefaultFilesOptions() : this(new SharedOptions()){}
  12:     public DefaultFilesOptions(SharedOptions sharedOptions) : base(sharedOptions)
  13:     {
  14:         this.DefaultFileNames = new List<string
> { "default.htm", "default.html", "index.htm", "index.html" };
  15:     }    
  16: }

我們同樣採用一種比較易於理解的形式重新定義這個DefaultFilesMiddleware型別以便於讀者朋友理解它具體採用的請求處理邏輯。如下面的程式碼片段所示,DefaultFilesMiddleware和DirectoryBrowserMiddleware一樣會對請求做相應的驗證。如果當前目錄下存在某個預設檔案,那麼它會將當前請求的URL修改成指向這個預設檔案的URL。值得一提的是,DefaultFilesMiddleware中介軟體要求訪問目錄的請求路勁必須以字元“/”作為字尾,否則會在目前的路徑上新增這個字尾並針對最終的路徑傳送一個重定向。

   1: public class DefaultFilesMiddleware
   2: {
   3:     private RequestDelegate         _next;
   4:     private DefaultFilesOptions     _options;
   5:   
   6:     public DefaultFilesMiddleware(RequestDelegate next, IHostingEnvironment env, IOptions<DefaultFilesOptions> options)
   7:     {
   8:         _next                     = next;
   9:         _options                  = options.Value;
  10:         _options.FileProvider     = _options.FileProvider ?? env.WebRootFileProvider;
  11:     }
  12:  
  13:     public async Task Invoke(HttpContext context)
  14:     {
  15:         //只處理GET和HEAD請求
  16:         if (!new string[] { "GET", "HEAD" }.Contains(context.Request.Method,StringComparer.OrdinalIgnoreCase))
  17:         {
  18:             await _next(context);
  19:             return;
  20:         }
  21:  
  22:         //檢驗當前路徑是否與註冊的請求路徑相匹配
  23:         PathString path = new PathString(context.Request.Path.Value.TrimEnd('/') + "/");
  24:         PathString subpath;
  25:         if (!path.StartsWithSegments(_options.RequestPath, out subpath))
  26:         {
  27:             await _next(context);
  28:             return;
  29:         }
  30:  
  31:         //檢驗目標目錄是否存在
  32:         if (!_options.FileProvider.GetDirectoryContents(subpath).Exists)
  33:         {
  34:             await _next(context);
  35:             return;
  36:         }
  37:  
  38:         //檢驗當前目錄是否包含預設檔案
  39:         foreach (var fileName in _options.DefaultFileNames)
  40:         {
  41:             if (_options.FileProvider.GetFileInfo($"{subpath}{fileName}").Exists)
  42:             {
  43:                 //如果當前路徑不以"/"作為字尾,會響應一個針對“標準”URL的重定向
  44:                 if (!context.Request.Path.Value.EndsWith("/"))
  45:                 {
  46:                     context.Response.StatusCode = 302;
  47:                     context.Response.GetTypedHeaders().Location = new Uri(path.Value + context.Request.QueryString);
  48:                     return;
  49:                 }
  50:                 //將針對目錄的URL更新為針對預設檔案的URL
  51:                 context.Request.Path = new PathString($"{context.Request.Path}{fileName}");
  52:             }
  53:         }
  54:         await _next(context);
  55:     }
  56: }

正是因為DefaultFilesMiddleware中介軟體採用URL重寫的方式來響應預設檔案,所以它最終依賴StaticFileMiddleware中介軟體來響應預設檔案,所以針對後者的註冊時必須的。也正是這個原因,這個中介軟體需要優先註冊以確保URL重寫發生在StaticFileMiddleware響應檔案之前。