<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          理解ASP.NET Core-文件服務器(File Server)

          共 17425字,需瀏覽 35分鐘

           ·

          2021-12-01 17:09

          ↓推薦關注↓


          系列文章
          《理解 ASP.NET Core - 配置(Configuration)》
          《理解 ASP.NET Core - 依賴注入》


          提供靜態(tài)文件


          靜態(tài)文件默認存放在 Web根目錄(Web Root) 中,路徑為 項目根目錄(Content Root) 下的wwwroot文件夾,也就是{Content Root}/wwwroot

          如果你調(diào)用了Host.CreateDefaultBuilder方法,那么在該方法中,會通過UseContentRoot方法,將程序當前工作目錄(Directory.GetCurrentDirectory())設置為項目根目錄。

          public?static?IHostBuilder?CreateHostBuilder(string[]?args)?=>
          ????Host.CreateDefaultBuilder(args)
          ????????.ConfigureWebHostDefaults(webBuilder?=>
          ????????{
          ????????????webBuilder.UseStartup();
          ????????});

          當然,你也可以通過UseWebRoot擴展方法將默認的路徑{Content Root}/wwwroot修改為自定義目錄(不過,你改它干啥捏?)

          public?static?IHostBuilder?CreateHostBuilder(string[]?args)?=>
          ????Host.CreateDefaultBuilder(args)
          ????????.ConfigureWebHostDefaults(webBuilder?=>
          ????????{
          ????????????//?配置靜態(tài)資源的根目錄為?mywwwroot,?默認為?wwwroot
          ????????????webBuilder.UseWebRoot("mywwwroot");

          ????????????webBuilder.UseStartup();
          ????????});

          為了方便,后面均使用 wwwroot 來表示W(wǎng)eb根目錄

          首先,我們先在 wwwroot 文件夾下創(chuàng)建一個名為 config.json 的文件,內(nèi)容隨便填寫

          注意,確保 wwwroot 下的文件的屬性為“如果較新則復制”或“始終復制”。

          接著,我們通過UseStaticFiles擴展方法,來注冊靜態(tài)文件中間件StaticFileMiddleware

          public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
          {
          ????app.UseStaticFiles();
          }

          現(xiàn)在,嘗試一下通過 http://localhost:5000/config.json 來獲取 wwwroot/config.json 的文件內(nèi)容吧

          如果你的項目中啟用SwaggerUI,那么你會發(fā)現(xiàn),即使你沒有手動通過調(diào)用UseStaticFiles()添加中間件,你也可以訪問 wwwroot 文件下的文件,這是因為 SwaggerUIMiddleware 中使用了 StaticFileMiddleware

          提供Web根目錄之外的文件

          上面我們已經(jīng)能夠提供 wwwroot 文件夾內(nèi)的靜態(tài)文件了,那如果我們的文件不在 wwwroot 文件夾內(nèi),那如何提供呢?

          很簡單,我們可以針對StaticFileMiddleware中間件進行一些額外的配置,了解一下配置項:

          public?abstract?class?SharedOptionsBase
          {
          ????//?用于自定義靜態(tài)文件的相對請求路徑
          ????public?PathString?RequestPath?{?get;?set;?}

          ????//?文件提供程序
          ????public?IFileProvider?FileProvider?{?get;?set;?}

          ????//?是否補全路徑末尾斜杠“/”,并重定向
          ????public?bool?RedirectToAppendTrailingSlash?{?get;?set;?}
          }

          public?class?StaticFileOptions?:?SharedOptionsBase
          {
          ????//?ContentType提供程序
          ????public?IContentTypeProvider?ContentTypeProvider?{?get;?set;?}
          ????
          ????//?如果?ContentTypeProvider?無法識別文件類型,是否仍作為默認文件類型提供
          ????public?bool?ServeUnknownFileTypes?{?get;?set;?}
          ????
          ????//?當?ServeUnknownFileTypes?=?true?時,若出現(xiàn)無法識別的文件類型,則將該屬性的值作為此文件的類型
          ????//?當?ServeUnknownFileTypes?=?true?時,必須賦值該屬性,才會生效
          ????public?string?DefaultContentType?{?get;?set;?}
          ????
          ????//?當注冊了HTTP響應壓縮中間件時,是否對文件進行壓縮
          ????public?HttpsCompressionMode?HttpsCompression?{?get;?set;?}?=?HttpsCompressionMode.Compress;
          ????
          ????//?在HTTP響應的?Status?Code?和?Headers?設置完畢之后,Body?寫入之前進行調(diào)用
          ????//?用于添加或更改?Headers
          ????public?Action?OnPrepareResponse?{?get;?set;?}
          }

          假設我們現(xiàn)在有這樣一個文件目錄結構:

          • wwwroot

            config.json

          • files

            file.json

          然后,除了用于提供 wwwroot 靜態(tài)文件的中間件外,我們還要注冊一個用于提供 files 靜態(tài)文件的中間件:

          public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
          {
          ????//?提供?wwwroot?靜態(tài)文件
          ????app.UseStaticFiles();

          ????//?提供?files?靜態(tài)文件
          ????app.UseStaticFiles(new?StaticFileOptions
          ????{
          ????????FileProvider?=?new?PhysicalFileProvider(Path.Combine(env.ContentRootPath,?"files")),
          ????????//?指定文件的訪問路徑,允許與?FileProvider?中的文件夾不同名
          ????????//?如果不指定,則可通過?http://localhost:5000/file.json?獲取,
          ????????//?如果指定,則需要通過?http://localhost:5000/files/file.json?獲取
          ????????RequestPath?=?"/files",
          ????????OnPrepareResponse?=?ctx?=>
          ????????{
          ????????????//?配置前端緩存?600s(為了后續(xù)示例的良好運行,建議先不要配置該Header)
          ????????????ctx.Context.Response.Headers.Add(HeaderNames.CacheControl,?"public,max-age=600");
          ????????}
          ????});
          }

          建議將公開訪問的文件放置到 wwwroot 目錄下,而將需要授權訪問的文件放置到其他目錄下(在調(diào)用UseAuthorization之后調(diào)用UseStaticFiles并指定文件目錄)

          提供目錄瀏覽

          上面,我們可以通過Url訪問某一個文件的內(nèi)容,而通過UseDirectoryBrowser,注冊DirectoryBrowserMiddleware中間件,可以讓我們在瀏覽器中以目錄的形式來訪問文件列表。

          另外,DirectoryBrowserMiddleware中間件的可配置項除了SharedOptionsBase中的之外,還有一個Formatter,用于自定義目錄視圖。

          public?class?DirectoryBrowserOptions?:?SharedOptionsBase
          {
          ????public?IDirectoryFormatter?Formatter?{?get;?set;?}
          }

          示例如下:

          public?void?ConfigureServices(IServiceCollection?services)
          {
          ????services.AddDirectoryBrowser();
          }

          public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
          {
          ????//?通過?http://localhost:5000,即可訪問?wwwroot?目錄
          ????app.UseDirectoryBrowser();
          ????
          ????//?通過?http://localhost:5000/files,即可訪問?files?目錄
          ????app.UseDirectoryBrowser(new?DirectoryBrowserOptions
          ????{
          ????????//?如果指定了沒有在?UseStaticFiles?中提供的文件目錄,雖然可以瀏覽文件列表,但是無法訪問文件內(nèi)容
          ????????FileProvider?=?new?PhysicalFileProvider(Path.Combine(env.ContentRootPath,?"files")),
          ????????//?這里一定要和?StaticFileOptions?中的?RequestPath?一致,否則會無法訪問文件
          ????????RequestPath?=?"/files"
          ????});
          }

          提供默認頁

          通過UseDefaultFiles,注冊DefaultFilesMiddleware中間件,允許在訪問靜態(tài)文件、但未提供文件名的情況下(即傳入的是一個目錄的路徑),提供默認頁的展示。

          注意:UseDefaultFiles必須在UseStaticFiles之前進行調(diào)用。因為DefaultFilesMiddleware僅僅負責重寫Url,實際上默認頁文件,仍然是通過StaticFilesMiddleware來提供的。

          默認情況下,該中間件會按照順序搜索文件目錄下的HTML頁面文件:

          • default.htm
          • default.html
          • index.htm
          • index.html

          另外,DefaultFilesMiddleware中間件的可配置項除了SharedOptionsBase中的之外,還有一個DefaultFileNames,是個列表,用于自定義默認頁的文件名,里面的默認值就是上面提到的4個文件名。

          public?class?DefaultFilesOptions?:?SharedOptionsBase
          {
          ????public?IList<string>?DefaultFileNames?{?get;?set;?}
          }

          示例如下:

          public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
          {
          ????//?會去?wwwroot?尋找?default.htm?、default.html?、index.htm?或?index.html?文件作為默認頁
          ????app.UseDefaultFiles();

          ????//?設置?files?目錄的默認頁
          ????var?defaultFilesOptions?=?new?DefaultFilesOptions();
          ????defaultFilesOptions.DefaultFileNames.Clear();
          ????//?指定默認頁名稱
          ????defaultFilesOptions.DefaultFileNames.Add("index1.html");
          ????//?指定請求路徑
          ????defaultFilesOptions.RequestPath?=?"/files";
          ????//?指定默認頁所在的目錄
          ????defaultFilesOptions.FileProvider?=?new?PhysicalFileProvider(Path.Combine(env.ContentRootPath,?"files"));

          ????app.UseDefaultFiles(defaultFilesOptions);
          }

          UseFileServer

          UseFileServer集成了UseStaticFilesUseDefaultFilesUseDirectoryBrowser的功能,用起來方便一些,也是我們項目中使用的首選擴展方法。

          先看一下FileServerOptions

          public?class?FileServerOptions?:?SharedOptionsBase
          {
          ????public?FileServerOptions()
          ????????:?base(new?SharedOptions())

          ????{
          ????????StaticFileOptions?=?new?StaticFileOptions(SharedOptions);
          ????????DirectoryBrowserOptions?=?new?DirectoryBrowserOptions(SharedOptions);
          ????????DefaultFilesOptions?=?new?DefaultFilesOptions(SharedOptions);
          ????????EnableDefaultFiles?=?true;
          ????}

          ????public?StaticFileOptions?StaticFileOptions?{?get;?private?set;?}

          ????public?DirectoryBrowserOptions?DirectoryBrowserOptions?{?get;?private?set;?}

          ????public?DefaultFilesOptions?DefaultFilesOptions?{?get;?private?set;?}

          ????//?默認禁用目錄瀏覽
          ????public?bool?EnableDirectoryBrowsing?{?get;?set;?}

          ????//?默認啟用默認頁(在構造函數(shù)中初始化的)
          ????public?bool?EnableDefaultFiles?{?get;?set;?}
          }

          可以看到,FileServerOptions包含了StaticFileOptionsDirectoryBrowserOptionsDefaultFilesOptions三個選項,可以針對StaticFileMiddlewareDirectoryBrowserMiddlewareDefaultFilesMiddleware進行自定義配置。另外,其默認啟用了靜態(tài)文件和默認頁,禁用了目錄瀏覽。

          下面舉個例子熟悉一下:

          假設文件目錄:

          • files

            • images

              1.jpg

            • file.json

            • myindex.html

          public?void?ConfigureServices(IServiceCollection?services)
          {
          ????//?如果將?EnableDirectoryBrowsing?設為?true,記得注冊服務
          ????services.AddDirectoryBrowser();
          }

          public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
          {???
          ????//?啟用?StaticFileMiddleware
          ????//?啟用?DefaultFilesMiddleware
          ????//?禁用?DirectoryBrowserMiddleware
          ????//?默認指向?wwwroot
          ????app.UseFileServer();
          ????
          ????//?針對?files?文件夾配置
          ????var?fileServerOptions?=?new?FileServerOptions
          ????{
          ????????FileProvider?=?new?PhysicalFileProvider(Path.Combine(env.ContentRootPath,?"files")),
          ????????RequestPath?=?"/files",
          ????????EnableDirectoryBrowsing?=?true
          ????};
          ????fileServerOptions.StaticFileOptions.OnPrepareResponse?=?ctx?=>
          ????{
          ????????//?配置緩存600s
          ????????ctx.Context.Response.Headers.Add(HeaderNames.CacheControl,?"public,max-age=600");
          ????};
          ????fileServerOptions.DefaultFilesOptions.DefaultFileNames.Clear();
          ????fileServerOptions.DefaultFilesOptions.DefaultFileNames.Add("myindex.html");
          ????app.UseFileServer(fileServerOptions);
          }

          當訪問 http://localhost:5000/files 時,由于在DefaultFilesOptions.DefaultFileNames中添加了文件名myindex.html,所以可以找到默認頁,此時會顯示默認頁的內(nèi)容。

          假如我們沒有在DefaultFilesOptions.DefaultFileNames中添加文件名myindex.html,那么便找不到默認頁,但由于啟用了DirectoryBrowsing,所以此時會展示文件列表。

          核心配置項

          FileProvider

          上面我們已經(jīng)見過PhysicalFileProvider了,它僅僅是眾多文件提供程序中的一種。所有的文件提供程序均實現(xiàn)了IFileProvider接口:

          public?interface?IFileProvider
          {
          ????//?獲取給定路徑的目錄信息,可枚舉該目錄中的所有文件
          ????IDirectoryContents?GetDirectoryContents(string?subpath);

          ????//?獲取給定路徑的文件信息
          ????IFileInfo?GetFileInfo(string?subpath);

          ????//?創(chuàng)建指定?filter?的?ChangeToken
          ????IChangeToken?Watch(string?filter);
          }

          public?interface?IDirectoryContents?:?IEnumerable<IFileInfo>,?IEnumerable
          {
          ????bool?Exists?{?get;?}
          }

          public?interface?IFileInfo
          {
          ????bool?Exists?{?get;?}

          ????bool?IsDirectory?{?get;?}?

          ????DateTimeOffset?LastModified?{?get;?}

          ????//?字節(jié)(bytes)長度
          ????//?如果是目錄或文件不存在,則是?-1
          ????long?Length?{?get;?}

          ????//?目錄或文件名,純文件名,不包括路徑
          ????string?Name?{?get;?}

          ????//?文件路徑,包含文件名
          ????//?如果文件無法直接訪問,則返回?null
          ????string?PhysicalPath?{?get;?}

          ????//?創(chuàng)建該文件只讀流
          ????Stream?CreateReadStream();
          }

          常用的文件提供程序有以下三種:

          • PhysicalFileProvider
          • ManifestEmbeddedFileProvider
          • CompositeFileProvider

          glob模式

          在介紹這三種文件提供程序之前,先說一下glob模式,即通配符模式。兩個通配符分別是***

          • *:匹配當前目錄層級(不包含子目錄)下的任何內(nèi)容、任何文件名或任何文件擴展名,可以通過/\.進行分隔。
          • **:匹配目錄多層級(包含子目錄)的任何內(nèi)容,用于遞歸匹配多層級目錄的多個文件。

          PhysicalFileProvider

          PhysicalFileProvider用于提供物理文件系統(tǒng)的訪問。該提供程序需要將文件路徑范圍限定在一個目錄及其子目錄中,不能訪問目錄外部的內(nèi)容。

          當實例化該文件提供程序時,需要提供一個絕對的目錄路徑,作為文件目錄的root。

          PhysicalFileProvider目錄或文件路徑不支持glob(通配符)模式。

          ManifestEmbeddedFileProvider

          ManifestEmbeddedFileProvider用于提供嵌入在程序集中的文件的訪問。

          可能你對這個嵌入文件比較陌生,沒關系,請按照下面的步驟來:

          安裝Nuget包:Install-Package Microsoft.Extensions.FileProviders.Embedded

          編輯.csproj文件:

          添加,并設置為true

          使用添加要嵌入的文件

          以下是 .csproj 文件的示例:

          <Project?Sdk="Microsoft.NET.Sdk.Web">
          ??<PropertyGroup>
          ????<TargetFramework>net5.0TargetFramework>
          ????<GenerateEmbeddedFilesManifest>trueGenerateEmbeddedFilesManifest>
          ??PropertyGroup>

          ??<ItemGroup>
          ????<PackageReference?Include="Microsoft.Extensions.FileProviders.Embedded"?Version="5.0.11"?/>
          ??ItemGroup>

          ??<ItemGroup>
          ????<EmbeddedResource?Include="files\**"?/>
          ??ItemGroup>
          Project>

          現(xiàn)在我們通過ManifestEmbeddedFileProvider來提供嵌入到程序集的 files 目錄下文件的訪問:

          public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
          {
          ?????var?fileServerOptions?=?new?FileServerOptions();
          ????fileServerOptions.StaticFileOptions.FileProvider?=?new?ManifestEmbeddedFileProvider(Assembly.GetExecutingAssembly(),?"/files");
          ????fileServerOptions.StaticFileOptions.RequestPath?=?"/files";

          ????app.UseFileServer(fileServerOptions);
          }

          現(xiàn)在,你可以通過 http://localhost:5000/files/file.json 來訪問文件了。

          CompositeFileProvider

          CompositeFileProvider用于將多種文件提供程序進行集成。

          如:

          public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
          {
          ????var?fileServerOptions?=?new?FileServerOptions();
          ????var?fileProvider?=?new?CompositeFileProvider(
          ????????env.WebRootFileProvider,
          ????????new?ManifestEmbeddedFileProvider(Assembly.GetExecutingAssembly(),?"/files")
          ????);
          ????fileServerOptions.StaticFileOptions.FileProvider?=?fileProvider;
          ????fileServerOptions.StaticFileOptions.RequestPath?=?"/composite";

          ????app.UseFileServer(fileServerOptions);
          }

          現(xiàn)在,你可以通過 http://localhost:5000/composite/file.json 來訪問文件了。

          ContentTypeProvider

          Http請求頭中的Content-Type大家一定很熟悉,ContentTypeProvider就是用來提供文件擴展名和MIME類型映射關系的。

          若我們沒有顯示指定ContentTypeProvider,則框架默認使用FileExtensionContentTypeProvider,其實現(xiàn)了接口IContentTypeProvider

          public?interface?IContentTypeProvider
          {
          ????//?嘗試根據(jù)文件路徑,獲取對應的?MIME?類型
          ????bool?TryGetContentType(string?subpath,?out?string?contentType);
          }

          public?class?FileExtensionContentTypeProvider?:?IContentTypeProvider
          {
          ????public?FileExtensionContentTypeProvider()
          ????????:?this(new?Dictionary<string,?string>(StringComparer.OrdinalIgnoreCase)

          ????????{
          ????????????//?...此處省略一萬字
          ????????}
          ????{
          ????}

          ????public?FileExtensionContentTypeProvider(IDictionary<string,?string>?mapping)
          ????{
          ????????Mappings?=?mapping;
          ????}

          ????public?IDictionary<string,?string>?Mappings?{?get;?private?set;?}

          ????public?bool?TryGetContentType(string?subpath,?out?string?contentType)
          ????{
          ????????string?extension?=?GetExtension(subpath);
          ????????if?(extension?==?null)
          ????????{
          ????????????contentType?=?null;
          ????????????return?false;
          ????????}
          ????????return?Mappings.TryGetValue(extension,?out?contentType);
          ????}

          ????private?static?string?GetExtension(string?path)
          ????{
          ????????//?沒有使用 Path.GetExtension()?的原因是:當路徑中存在無效字符時,其會拋出異常,而這里不應拋出異常。

          ????????if?(string.IsNullOrWhiteSpace(path))
          ????????{
          ????????????return?null;
          ????????}

          ????????int?index?=?path.LastIndexOf('.');
          ????????if?(index?0)
          ????????{
          ????????????return?null;
          ????????}

          ????????return?path.Substring(index);
          ????}
          }

          FileExtensionContentTypeProvider的無參構造函數(shù)中,默認添加了380種已知的文件擴展名和MIME類型的映射,存放在Mappings屬性中。你也可以添加自定義的映射,或移除不想要的映射。

          核心中間件

          StaticFileMiddleware

          通過UseStaticFiles擴展方法,可以方便的注冊StaticFileMiddleware中間件:

          public?static?class?StaticFileExtensions
          {
          ????public?static?IApplicationBuilder?UseStaticFiles(this?IApplicationBuilder?app)
          ????{
          ????????return?app.UseMiddleware();
          ????}
          ????
          ????public?static?IApplicationBuilder?UseStaticFiles(this?IApplicationBuilder?app,?string?requestPath)
          ????{
          ????????return?app.UseStaticFiles(new?StaticFileOptions
          ????????{
          ????????????RequestPath?=?new?PathString(requestPath)
          ????????});
          ????}

          ????public?static?IApplicationBuilder?UseStaticFiles(this?IApplicationBuilder?app,?StaticFileOptions?options)
          ????{
          ????????return?app.UseMiddleware(Options.Create(options));
          ????}
          }

          緊接著查看StaticFileMiddlewareInvoke方法:

          public?class?StaticFileMiddleware
          {
          ????private?readonly?StaticFileOptions?_options;
          ????private?readonly?PathString?_matchUrl;
          ????private?readonly?RequestDelegate?_next;
          ????private?readonly?ILogger?_logger;
          ????private?readonly?IFileProvider?_fileProvider;
          ????private?readonly?IContentTypeProvider?_contentTypeProvider;

          ????public?StaticFileMiddleware(RequestDelegate?next,?IWebHostEnvironment?hostingEnv,?IOptions?options,?ILoggerFactory?loggerFactory)
          ????{
          ????????_next?=?next;
          ????????_options?=?options.Value;
          ????????//?若未指定?ContentTypeProvider,則默認使用?FileExtensionContentTypeProvider
          ????????_contentTypeProvider?=?_options.ContentTypeProvider????new?FileExtensionContentTypeProvider();
          ????????//?若未指定?FileProvider,則默認使用?hostingEnv.WebRootFileProvider
          ????????_fileProvider?=?_options.FileProvider????Helpers.ResolveFileProvider(hostingEnv);
          ????????_matchUrl?=?_options.RequestPath;
          ????????_logger?=?loggerFactory.CreateLogger();
          ????}

          ????public?Task?Invoke(HttpContext?context)
          ????{
          ????????//?若已匹配到?Endpoint,則跳過
          ????????if?(!ValidateNoEndpoint(context))
          ????????{
          ????????????_logger.EndpointMatched();
          ????????}
          ????????//?若HTTP請求方法不是?Get,也不是?Head,則跳過
          ????????else?if?(!ValidateMethod(context))
          ????????{
          ????????????_logger.RequestMethodNotSupported(context.Request.Method);
          ????????}
          ????????//?如果請求路徑不匹配,則跳過
          ????????else?if?(!ValidatePath(context,?_matchUrl,?out?var?subPath))
          ????????{
          ????????????_logger.PathMismatch(subPath);
          ????????}
          ????????//?如果?ContentType?不受支持,則跳過
          ????????else?if?(!LookupContentType(_contentTypeProvider,?_options,?subPath,?out?var?contentType))
          ????????{
          ????????????_logger.FileTypeNotSupported(subPath);
          ????????}
          ????????else
          ????????{
          ????????????//?嘗試提供靜態(tài)文件
          ????????????return?TryServeStaticFile(context,?contentType,?subPath);
          ????????}

          ????????return?_next(context);
          ????}

          ????private?static?bool?ValidateNoEndpoint(HttpContext?context)?=>?context.GetEndpoint()?==?null;

          ????private?static?bool?ValidateMethod(HttpContext?context)?=>?Helpers.IsGetOrHeadMethod(context.Request.Method);

          ????internal?static?bool?ValidatePath(HttpContext?context,?PathString?matchUrl,?out?PathString?subPath)?=>?Helpers.TryMatchPath(context,?matchUrl,?forDirectory:?false,?out?subPath);

          ????internal?static?bool?LookupContentType(IContentTypeProvider?contentTypeProvider,?StaticFileOptions?options,?PathString?subPath,?out?string?contentType)
          ????{
          ????????//?查看?Provider?中是否支持該?ContentType
          ????????if?(contentTypeProvider.TryGetContentType(subPath.Value,?out?contentType))
          ????????{
          ????????????return?true;
          ????????}

          ????????//?如果提供未知文件類型,則將其設置為默認?ContentType
          ????????if?(options.ServeUnknownFileTypes)
          ????????{
          ????????????contentType?=?options.DefaultContentType;
          ????????????return?true;
          ????????}

          ????????return?false;
          ????}

          ????private?Task?TryServeStaticFile(HttpContext?context,?string?contentType,?PathString?subPath)
          ????{
          ????????var?fileContext?=?new?StaticFileContext(context,?_options,?_logger,?_fileProvider,?contentType,?subPath);

          ????????//?如果文件不存在,則跳過
          ????????if?(!fileContext.LookupFileInfo())
          ????????{
          ????????????_logger.FileNotFound(fileContext.SubPath);
          ????????}
          ????????else
          ????????{
          ????????????//?若文件存在,則提供該靜態(tài)文件
          ????????????return?fileContext.ServeStaticFile(context,?_next);
          ????????}

          ????????return?_next(context);
          ????}
          }

          DirectoryBrowserMiddleware

          通過UseDirectoryBrowser擴展方法,可以方便的注冊DirectoryBrowserMiddleware中間件:

          public?static?class?DirectoryBrowserExtensions
          {
          ????public?static?IApplicationBuilder?UseDirectoryBrowser(this?IApplicationBuilder?app)
          ????{
          ????????return?app.UseMiddleware();
          ????}

          ????public?static?IApplicationBuilder?UseDirectoryBrowser(this?IApplicationBuilder?app,?string?requestPath)
          ????{
          ????????return?app.UseDirectoryBrowser(new?DirectoryBrowserOptions
          ????????{
          ????????????RequestPath?=?new?PathString(requestPath)
          ????????});
          ????}

          ????public?static?IApplicationBuilder?UseDirectoryBrowser(this?IApplicationBuilder?app,?DirectoryBrowserOptions?options)
          ????{
          ????????return?app.UseMiddleware(Options.Create(options));
          ????}
          }

          緊接著查看DirectoryBrowserMiddlewareInvoke方法:

          public?class?DirectoryBrowserMiddleware
          {
          ????private?readonly?DirectoryBrowserOptions?_options;
          ????private?readonly?PathString?_matchUrl;
          ????private?readonly?RequestDelegate?_next;
          ????private?readonly?IDirectoryFormatter?_formatter;
          ????private?readonly?IFileProvider?_fileProvider;

          ????public?DirectoryBrowserMiddleware(RequestDelegate?next,?IWebHostEnvironment?hostingEnv,?IOptions?options)
          ????????:?this(next,?hostingEnv,?HtmlEncoder.Default,?options)

          ????{
          ????}

          ????public?DirectoryBrowserMiddleware(RequestDelegate?next,?IWebHostEnvironment?hostingEnv,?HtmlEncoder?encoder,?IOptions?options)
          ????{
          ????????_next?=?next;
          ????????_options?=?options.Value;
          ????????//?若未指定?FileProvider,則默認使用?hostingEnv.WebRootFileProvider
          ????????_fileProvider?=?_options.FileProvider????Helpers.ResolveFileProvider(hostingEnv);
          ????????_formatter?=?_options.Formatter????new?HtmlDirectoryFormatter(encoder);
          ????????_matchUrl?=?_options.RequestPath;
          ????}

          ????public?Task?Invoke(HttpContext?context)
          ????{
          ????????//?若已匹配到?Endpoint,則跳過
          ????????//?若HTTP請求方法不是?Get,也不是?Head,則跳過
          ????????//?如果請求路徑不匹配,則跳過
          ????????//?若文件目錄不存在,則跳過
          ????????if?(context.GetEndpoint()?==?null
          ????????????&&?Helpers.IsGetOrHeadMethod(context.Request.Method)
          ????????????&&?Helpers.TryMatchPath(context,?_matchUrl,?forDirectory:?true,?subpath:?out?var?subpath)
          ????????????&&?TryGetDirectoryInfo(subpath,?out?var?contents))
          ????????{
          ????????????if?(_options.RedirectToAppendTrailingSlash?&&?!Helpers.PathEndsInSlash(context.Request.Path))
          ????????????{
          ????????????????Helpers.RedirectToPathWithSlash(context);
          ????????????????return?Task.CompletedTask;
          ????????????}

          ????????????//?生成文件瀏覽視圖
          ????????????return?_formatter.GenerateContentAsync(context,?contents);
          ????????}

          ????????return?_next(context);
          ????}

          ????private?bool?TryGetDirectoryInfo(PathString?subpath,?out?IDirectoryContents?contents)
          ????{
          ????????contents?=?_fileProvider.GetDirectoryContents(subpath.Value);
          ????????return?contents.Exists;
          ????}
          }

          DefaultFilesMiddleware

          通過UseDefaultFiles擴展方法,可以方便的注冊DefaultFilesMiddleware中間件:

          public?static?class?DefaultFilesExtensions
          {
          ????public?static?IApplicationBuilder?UseDefaultFiles(this?IApplicationBuilder?app)
          ????{
          ????????return?app.UseMiddleware();
          ????}

          ????public?static?IApplicationBuilder?UseDefaultFiles(this?IApplicationBuilder?app,?string?requestPath)
          ????{
          ????????return?app.UseDefaultFiles(new?DefaultFilesOptions
          ????????{
          ????????????RequestPath?=?new?PathString(requestPath)
          ????????});
          ????}

          ????public?static?IApplicationBuilder?UseDefaultFiles(this?IApplicationBuilder?app,?DefaultFilesOptions?options)
          ????{
          ????????return?app.UseMiddleware(Options.Create(options));
          ????}
          }

          緊接著查看DefaultFilesMiddlewareInvoke方法:

          public?class?DefaultFilesMiddleware
          {
          ????private?readonly?DefaultFilesOptions?_options;
          ????private?readonly?PathString?_matchUrl;
          ????private?readonly?RequestDelegate?_next;
          ????private?readonly?IFileProvider?_fileProvider;

          ????public?DefaultFilesMiddleware(RequestDelegate?next,?IWebHostEnvironment?hostingEnv,?IOptions?options)
          ????{
          ????????_next?=?next;
          ????????_options?=?options.Value;
          ????????//?若未指定?FileProvider,則默認使用?hostingEnv.WebRootFileProvider
          ????????_fileProvider?=?_options.FileProvider????Helpers.ResolveFileProvider(hostingEnv);
          ????????_matchUrl?=?_options.RequestPath;
          ????}
          ????
          ????public?Task?Invoke(HttpContext?context)
          ????{
          ????????//?若已匹配到?Endpoint,則跳過
          ????????//?若HTTP請求方法不是?Get,也不是?Head,則跳過
          ????????//?如果請求路徑不匹配,則跳過
          ????????if?(context.GetEndpoint()?==?null
          ????????????&&?Helpers.IsGetOrHeadMethod(context.Request.Method)
          ????????????&&?Helpers.TryMatchPath(context,?_matchUrl,?forDirectory:?true,?subpath:?out?var?subpath))
          ????????{
          ????????????var?dirContents?=?_fileProvider.GetDirectoryContents(subpath.Value);
          ????????????if?(dirContents.Exists)
          ????????????{
          ????????????????for?(int?matchIndex?=?0;?matchIndex?????????????????{
          ????????????????????string?defaultFile?=?_options.DefaultFileNames[matchIndex];
          ????????????????????var?file?=?_fileProvider.GetFileInfo(subpath.Value?+?defaultFile);
          ????????????????????
          ????????????????????//?找到了默認頁
          ????????????????????if?(file.Exists)
          ????????????????????{
          ????????????????????????if?(_options.RedirectToAppendTrailingSlash?&&?!Helpers.PathEndsInSlash(context.Request.Path))
          ????????????????????????{
          ????????????????????????????Helpers.RedirectToPathWithSlash(context);
          ????????????????????????????return?Task.CompletedTask;
          ????????????????????????}
          ????????????????????????
          ????????????????????????//?重寫為默認頁的Url,后續(xù)通過?StaticFileMiddleware?提供該頁面
          ????????????????????????context.Request.Path?=?new?PathString(Helpers.GetPathValueWithSlash(context.Request.Path)?+?defaultFile);
          ????????????????????????break;
          ????????????????????}
          ????????????????}
          ????????????}
          ????????}

          ????????return?_next(context);
          ????}
          }

          FileServer

          FileServer并不是某個具體的中間件,它的實現(xiàn)還是依賴了StaticFileMiddlewareDirectoryBrowserMiddlewareDefaultFilesMiddleware這3個中間件。不過,我們可以看一下UseFileServer里的邏輯:

          public?static?class?FileServerExtensions
          {
          ????public?static?IApplicationBuilder?UseFileServer(this?IApplicationBuilder?app)
          ????{
          ????????return?app.UseFileServer(new?FileServerOptions());
          ????}

          ????public?static?IApplicationBuilder?UseFileServer(this?IApplicationBuilder?app,?bool?enableDirectoryBrowsing)
          ????{
          ????????return?app.UseFileServer(new?FileServerOptions
          ????????{
          ????????????EnableDirectoryBrowsing?=?enableDirectoryBrowsing
          ????????});
          ????}

          ????public?static?IApplicationBuilder?UseFileServer(this?IApplicationBuilder?app,?string?requestPath)
          ????{
          ????????return?app.UseFileServer(new?FileServerOptions
          ????????{
          ????????????RequestPath?=?new?PathString(requestPath)
          ????????});
          ????}

          ????public?static?IApplicationBuilder?UseFileServer(this?IApplicationBuilder?app,?FileServerOptions?options)
          ????{
          ????????//?啟用默認頁
          ????????if?(options.EnableDefaultFiles)
          ????????{
          ????????????app.UseDefaultFiles(options.DefaultFilesOptions);
          ????????}

          ????????//?啟用目錄瀏覽
          ????????if?(options.EnableDirectoryBrowsing)
          ????????{
          ????????????app.UseDirectoryBrowser(options.DirectoryBrowserOptions);
          ????????}

          ????????return?app.UseStaticFiles(options.StaticFileOptions);
          ????}
          }

          FileProvider in IWebHostingEnvironment

          在接口IHostingEnvironment中,包含ContentRootFileProviderWebRootFileProvider兩個文件提供程序。下面我們就看一下他們是如何被初始化的。

          internal?class?GenericWebHostBuilder?:?IWebHostBuilder,?ISupportsStartup,?ISupportsUseDefaultServiceProvider
          {
          ????private?WebHostBuilderContext?GetWebHostBuilderContext(HostBuilderContext?context)
          ????{
          ????????if?(!context.Properties.TryGetValue(typeof(WebHostBuilderContext),?out?var?contextVal))
          ????????{
          ????????????var?options?=?new?WebHostOptions(context.Configuration,?Assembly.GetEntryAssembly()?.GetName().Name);
          ????????????var?webHostBuilderContext?=?new?WebHostBuilderContext
          ????????????{
          ????????????????Configuration?=?context.Configuration,
          ????????????????HostingEnvironment?=?new?HostingEnvironment(),
          ????????????};
          ????????????
          ????????????//?重點在這里,看這個?Initialize?方法
          ????????????webHostBuilderContext.HostingEnvironment.Initialize(context.HostingEnvironment.ContentRootPath,?options);
          ????????????context.Properties[typeof(WebHostBuilderContext)]?=?webHostBuilderContext;
          ????????????context.Properties[typeof(WebHostOptions)]?=?options;
          ????????????return?webHostBuilderContext;
          ????????}

          ????????var?webHostContext?=?(WebHostBuilderContext)contextVal;
          ????????webHostContext.Configuration?=?context.Configuration;
          ????????return?webHostContext;
          ????}
          }

          internal?static?class?HostingEnvironmentExtensions
          {
          ????internal?static?void?Initialize(this?IWebHostEnvironment?hostingEnvironment,?string?contentRootPath,?WebHostOptions?options)
          ????{
          ????????hostingEnvironment.ApplicationName?=?options.ApplicationName;
          ????????hostingEnvironment.ContentRootPath?=?contentRootPath;
          ????????//?初始化?ContentRootFileProvider
          ????????hostingEnvironment.ContentRootFileProvider?=?new?PhysicalFileProvider(hostingEnvironment.ContentRootPath);

          ????????var?webRoot?=?options.WebRoot;
          ????????if?(webRoot?==?null)
          ????????{
          ????????????//?如果?/wwwroot?目錄存在,則設置為Web根目錄
          ????????????var?wwwroot?=?Path.Combine(hostingEnvironment.ContentRootPath,?"wwwroot");
          ????????????if?(Directory.Exists(wwwroot))
          ????????????{
          ????????????????hostingEnvironment.WebRootPath?=?wwwroot;
          ????????????}
          ????????}
          ????????else
          ????????{
          ????????????hostingEnvironment.WebRootPath?=?Path.Combine(hostingEnvironment.ContentRootPath,?webRoot);
          ????????}

          ????????if?(!string.IsNullOrEmpty(hostingEnvironment.WebRootPath))
          ????????{
          ????????????hostingEnvironment.WebRootPath?=?Path.GetFullPath(hostingEnvironment.WebRootPath);
          ????????????if?(!Directory.Exists(hostingEnvironment.WebRootPath))
          ????????????{
          ????????????????Directory.CreateDirectory(hostingEnvironment.WebRootPath);
          ????????????}
          ????????????
          ????????????//?初始化?WebRootFileProvider
          ????????????hostingEnvironment.WebRootFileProvider?=?new?PhysicalFileProvider(hostingEnvironment.WebRootPath);
          ????????}
          ????????else
          ????????{
          ????????????hostingEnvironment.WebRootFileProvider?=?new?NullFileProvider();
          ????????}

          ????????hostingEnvironment.EnvironmentName?=
          ????????????options.Environment???
          ????????????hostingEnvironment.EnvironmentName;
          ????}
          }

          注意

          • 使用UseDirectoryBrowserUseStaticFiles提供文件瀏覽和訪問時,URL 受大小寫和基礎文件系統(tǒng)字符的限制。例如,Windows 不區(qū)分大小寫,但 macOS 和 Linux 區(qū)分大小寫。
          • 如果使用 IIS 托管應用,那么 IIS 自帶的靜態(tài)文件處理器是不工作的,均是使用 ASP.NET Core Module 進行處理的,包括靜態(tài)文件處理。

          小結

          • 使用UseFileServer擴展方法提供文件瀏覽和訪問,其集成了UseStaticFiles、UseDirectoryBrowser和UseDefaultFiles三個中間件的功能。
            • UseStaticFiles:注冊StaticFilesMiddleware,提供文件訪問
            • UseDirectoryBrowser:注冊DirectoryBrowserMiddleware,提供文件目錄瀏覽
            • UseDefaultFiles:注冊DefaultFilesMiddleware,當Url未指定訪問的文件名時,提供默認頁。
          • 文件提供程序均實現(xiàn)了接口IFileProvider,常用的文件提供程序有以下三種:
            • PhysicalFileProvider:提供物理文件系統(tǒng)的訪問
            • ManifestEmbeddedFileProvider:提供嵌入在程序集中的文件的訪問
            • CompositeFileProvider:用于將多種文件提供程序進行集成。
          • 可通過IWebHostingEnvironment獲取ContentRootFileProvider(默認目錄為項目根目錄)和WebRootFileProvider(默認目錄為Web根目錄)。


          轉(zhuǎn)自:xiaoxiaotank

          鏈接:cnblogs.com/xiaoxiaotank/p/15496538.html


          - EOF -

          推薦閱讀??點擊標題可跳轉(zhuǎn)
          使用 .NET6 打造動態(tài) API
          Refit結合Polly訪問ASP.NET Core WebAPI
          使用 ASP.NET Core 微服務 – 終極詳細指南?


          看完本文有收獲?請轉(zhuǎn)發(fā)分享給更多人

          推薦關注「DotNet」,提升.Net技能?

          點贊和在看就是最大的支持??

          瀏覽 50
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕第一页在线播放 | 看操逼逼| 黑人大屌在线 | 91影院体验区 | 国产精品久久久一区二区三区四区 |