Note: This article is part of the understanding ASP.NET Core series. Please check the top blog or Click here to view the full-text catalog
Provide static files
Static files are stored in the Web Root directory (Web Root) by default, and the path is the wwwroot folder under the project root directory (Content Root), that is, {Content Root}/wwwroot.
If you call the Host.CreateDefaultBuilder method, the current working directory (Directory.GetCurrentDirectory()) of the program will be set as the project root directory through the UseContentRoot method. You can view the details host A section.
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
Of course, you can also change the default path {Content Root}/wwwroot to a custom directory through the UseWebRoot extension method (but why do you change it?)
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { // The root directory of the configuration static resource is mywwwroot, and the default is wwwroot webBuilder.UseWebRoot("mywwwroot"); webBuilder.UseStartup<Startup>(); });
For convenience, wwwroot is used later to represent the Web root directory
First, we create a file named config.json in the wwwroot folder, and fill in the content casually
Note that make sure that the attribute of the file under wwwroot is copy if newer or always copy.
Next, we register the static file middleware StaticFileMiddleware through the UseStaticFiles extension method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseStaticFiles(); }
Now, try to pass http://localhost:5000/config.json To get the contents of the wwwroot/config.json file
If SwaggerUI is enabled in your project, you will find that even if you do not manually add middleware by calling UseStaticFiles(), you can access the files under the wwwroot file, because SwaggerUIMiddleware uses StaticFileMiddleware
Provide files outside the Web root directory
Above, we have been able to provide the static files in the wwwroot folder. If our files are not in the wwwroot folder, how can we provide them?
Very simply, we can make some additional configurations for the StaticFileMiddleware middleware. Learn about the configuration items:
public abstract class SharedOptionsBase { // Relative request path for custom static files public PathString RequestPath { get; set; } // File provider public IFileProvider FileProvider { get; set; } // Do you want to complete the slash "/" at the end of the path and redirect it public bool RedirectToAppendTrailingSlash { get; set; } } public class StaticFileOptions : SharedOptionsBase { // ContentType provider public IContentTypeProvider ContentTypeProvider { get; set; } // If the ContentTypeProvider does not recognize the file type, do you still provide it as the default file type public bool ServeUnknownFileTypes { get; set; } // When serverunknownfiletypes = true, if an unrecognized file type occurs, the value of this attribute will be taken as the file type // When serverunknownfiletypes = true, this attribute must be assigned to take effect public string DefaultContentType { get; set; } // Whether to compress the file when the HTTP response compression middleware is registered public HttpsCompressionMode HttpsCompression { get; set; } = HttpsCompressionMode.Compress; // After setting the Status Code and Headers of the HTTP response, call before writing the Body // Use to add or change Headers public Action<StaticFileResponseContext> OnPrepareResponse { get; set; } }
Suppose we now have such a file directory structure:
- wwwroot
- config.json
- files
- file.json
Then, in addition to the middleware for providing wwwroot static files, we also need to register a middleware for providing files static files:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // Provide wwwroot static file app.UseStaticFiles(); // Provide files static files app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "files")), // Specify the access path of the file. It is allowed to have different names from the folder in the FileProvider // If not specified, you can use http://localhost:5000/file.json obtain, // If specified, you need to pass http://localhost:5000/files/file.json obtain RequestPath = "/files", OnPrepareResponse = ctx => { // Configure the front-end cache for 600s (for the good operation of subsequent examples, it is recommended not to configure the Header first) ctx.Context.Response.Headers.Add(HeaderNames.CacheControl, "public,max-age=600"); } }); }
It is recommended to place the publicly accessed files in the wwwroot directory and place the files authorized to be accessed to other directories (calling UseStaticFiles after calling UseAuthorization and specifying the file directory).
Provide directory browsing
Above, we can access the contents of a file through Url, and register DirectoryBrowserMiddleware through UseDirectoryBrowser, so that we can access the file list in the form of directory in the browser.
In addition, the configurable items of DirectoryBrowserMiddleware include a Formatter in addition to those in sharedoptions base, which is used to customize the directory view.
public class DirectoryBrowserOptions : SharedOptionsBase { public IDirectoryFormatter Formatter { get; set; } }
Examples are as follows:
public void ConfigureServices(IServiceCollection services) { services.AddDirectoryBrowser(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // Pass http://localhost:5000 , you can access the wwwroot directory app.UseDirectoryBrowser(); // Pass http://localhost:5000/files , you can access the files directory app.UseDirectoryBrowser(new DirectoryBrowserOptions { // If you specify a file directory that is not provided in UseStaticFiles, you can browse the file list, but you cannot access the file content FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "files")), // This must be consistent with the RequestPath in StaticFileOptions, otherwise the file will not be accessible RequestPath = "/files" }); }
Provide default page
Register the DefaultFilesMiddleware through UseDefaultFiles, which allows you to provide the display of default pages when accessing static files without providing a file name (that is, the path of a directory is passed in).
Note: UseDefaultFiles must be called before UseStaticFiles. Because DefaultFilesMiddleware is only responsible for rewriting URLs, in fact, the default page file is still provided through StaticFilesMiddleware.
By default, the middleware will search the HTML page files under the file directory in order:
- default.htm
- default.html
- index.htm
- index.html
In addition, in addition to the configurable items in SharedOptionsBase, the DefaultFileNames in the DefaultFilesMiddleware middleware also has a list, which is used to customize the file names of the default page. The default values in it are the four file names mentioned above.
public class DefaultFilesOptions : SharedOptionsBase { public IList<string> DefaultFileNames { get; set; } }
Examples are as follows:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // You will go to wwwroot to find the default.htm, default.html, index.htm, or index.html file as the default page app.UseDefaultFiles(); // Set the default page for the files directory var defaultFilesOptions = new DefaultFilesOptions(); defaultFilesOptions.DefaultFileNames.Clear(); // Specifies the default page name defaultFilesOptions.DefaultFileNames.Add("index1.html"); // Specify request path defaultFilesOptions.RequestPath = "/files"; // Specify the directory where the default page is located defaultFilesOptions.FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "files")); app.UseDefaultFiles(defaultFilesOptions); }
UseFileServer
UseFileServer integrates the functions of UseStaticFiles, UseDefaultFiles and UseDirectoryBrowser, which is more convenient to use. It is also the preferred extension method used in our project.
Let's first look at fileserver options:
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; } // Directory browsing is disabled by default public bool EnableDirectoryBrowsing { get; set; } // Enable default page by default (initialized in constructor) public bool EnableDefaultFiles { get; set; } }
As you can see, fileserver options includes three options: StaticFileOptions, DirectoryBrowserOptions and DefaultFilesOptions, which can be customized for StaticFileMiddleware, DirectoryBrowserMiddleware and DefaultFilesMiddleware. In addition, static files and default pages are enabled by default, and directory browsing is disabled.
Here is an example:
Assumed file directory:
- files
- images
- 1.jpg
- file.json
- myindex.html
- images
public void ConfigureServices(IServiceCollection services) { // If EnableDirectoryBrowsing is set to true, remember to register the service services.AddDirectoryBrowser(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // Enable StaticFileMiddleware // Enable DefaultFilesMiddleware // Disable DirectoryBrowserMiddleware // The default point is wwwroot app.UseFileServer(); // Configuration for files folder var fileServerOptions = new FileServerOptions { FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "files")), RequestPath = "/files", EnableDirectoryBrowsing = true }; fileServerOptions.StaticFileOptions.OnPrepareResponse = ctx => { // Configure cache for 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); }
When visiting http://localhost:5000/files Because the file name myindex.html is added to DefaultFilesOptions.DefaultFileNames, the default page can be found and the content of the default page will be displayed.
If we don't add the file name myindex.html in DefaultFilesOptions.DefaultFileNames, we can't find the default page, but because DirectoryBrowsing is enabled, the file list will be displayed at this time.
Core configuration item
FileProvider
We have seen the PhysicalFileProvider above. It is just one of many file providers. All file providers implement IFileProvider interface:
public interface IFileProvider { // Get the directory information of the given path, and you can enumerate all files in the directory IDirectoryContents GetDirectoryContents(string subpath); // Gets the file information for the given path IFileInfo GetFileInfo(string subpath); // Creates a ChangeToken for the specified filter 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; } // Byte length // - 1 if the directory or file does not exist long Length { get; } // Directory or file name, pure file name, excluding path string Name { get; } // File path, including file name // Returns null if the file is not directly accessible string PhysicalPath { get; } // Create a read-only stream of this file Stream CreateReadStream(); }
There are three common file providers:
- PhysicalFileProvider
- ManifestEmbeddedFileProvider
- CompositeFileProvider
glob mode
Before introducing these three file providers, let's talk about glob mode, that is, wildcard mode. The two wildcards are * and * *.
- *: matches any content, any file name or any file extension under the current directory level (excluding subdirectories), which can be separated by /, \ and.
- **: matches any content of a multi-level directory (including subdirectories), which is used to recursively match multiple files of a multi-level directory.
PhysicalFileProvider
The PhysicalFileProvider is used to provide access to the physical file system. The provider needs to limit the file path range to a directory and its subdirectories, and cannot access the contents outside the directory.
When instantiating the file provider, you need to provide an absolute directory path as the root of the file directory.
glob (wildcard) mode is not supported for PhysicalFileProvider directory or file path.
ManifestEmbeddedFileProvider
The ManifestEmbeddedFileProvider is used to provide access to files embedded in an assembly.
You may be unfamiliar with this embedded file. It doesn't matter. Please follow the following steps:
- Install Nuget package: install package microsoft.extensions.fileproviders.embedded
- To edit a. csproj file:
- Add < generateembeddedfilesmanifest >, and set it to true
- Use < embeddedresource > to add files to embed
The following is an example of a. csproj file:
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="5.0.11" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="files\**" /> </ItemGroup> </Project>
Now we provide access to the files embedded in the files directory of the assembly through the ManifestEmbeddedFileProvider:
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); }
Now, you can pass http://localhost:5000/files/file.json To access the file.
CompositeFileProvider
CompositeFileProvider is used to integrate multiple file providers.
For example:
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); }
Now, you can pass http://localhost:5000/composite/file.json To access the file.
ContentTypeProvider
You must be familiar with the content type in the Http request header. The ContentTypeProvider is used to provide the mapping relationship between file extension and MIME type.
If we do not display the specified ContentTypeProvider, the framework uses FileExtensionContentTypeProvider by default, which implements the interface IContentTypeProvider:
public interface IContentTypeProvider { // Try to get the corresponding MIME type according to the file path bool TryGetContentType(string subpath, out string contentType); } public class FileExtensionContentTypeProvider : IContentTypeProvider { public FileExtensionContentTypeProvider() : this(new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { // ... omit 10000 words here } { } 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) { // The reason why Path.GetExtension() is not used is that when there are invalid characters in the path, it will throw an exception, which should not be thrown here. if (string.IsNullOrWhiteSpace(path)) { return null; } int index = path.LastIndexOf('.'); if (index < 0) { return null; } return path.Substring(index); } }
In the parameterless constructor of FileExtensionContentTypeProvider, 380 known file extension and MIME type Mappings are added by default and stored in the Mappings property. You can also add custom Mappings or remove unwanted Mappings.
Core middleware
StaticFileMiddleware
Using the UseStaticFiles extension method, you can easily register StaticFileMiddleware:
public static class StaticFileExtensions { public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app) { return app.UseMiddleware<StaticFileMiddleware>(); } 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<StaticFileMiddleware>(Options.Create(options)); } }
Next, check the Invoke method of StaticFileMiddleware:
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<StaticFileOptions> options, ILoggerFactory loggerFactory) { _next = next; _options = options.Value; // If no ContentTypeProvider is specified, FileExtensionContentTypeProvider is used by default _contentTypeProvider = _options.ContentTypeProvider ?? new FileExtensionContentTypeProvider(); // If no FileProvider is specified, hostingEnv.WebRootFileProvider is used by default _fileProvider = _options.FileProvider ?? Helpers.ResolveFileProvider(hostingEnv); _matchUrl = _options.RequestPath; _logger = loggerFactory.CreateLogger<StaticFileMiddleware>(); } public Task Invoke(HttpContext context) { // If the Endpoint has been matched, skip if (!ValidateNoEndpoint(context)) { _logger.EndpointMatched(); } // If the HTTP request method is neither Get nor Head, skip else if (!ValidateMethod(context)) { _logger.RequestMethodNotSupported(context.Request.Method); } // If the request paths do not match, skip else if (!ValidatePath(context, _matchUrl, out var subPath)) { _logger.PathMismatch(subPath); } // Skip if ContentType is not supported else if (!LookupContentType(_contentTypeProvider, _options, subPath, out var contentType)) { _logger.FileTypeNotSupported(subPath); } else { // Attempt to provide a static file 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) { // Check whether the ContentType is supported in the Provider if (contentTypeProvider.TryGetContentType(subPath.Value, out contentType)) { return true; } // If an unknown file type is provided, it is set as the default 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); // Skip if file does not exist if (!fileContext.LookupFileInfo()) { _logger.FileNotFound(fileContext.SubPath); } else { // If the file exists, the static file is provided return fileContext.ServeStaticFile(context, _next); } return _next(context); } }
DirectoryBrowserMiddleware
The DirectoryBrowserMiddleware can be easily registered through the UseDirectoryBrowser extension method:
public static class DirectoryBrowserExtensions { public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder app) { return app.UseMiddleware<DirectoryBrowserMiddleware>(); } 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<DirectoryBrowserMiddleware>(Options.Create(options)); } }
Next, check the Invoke method of DirectoryBrowserMiddleware:
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<DirectoryBrowserOptions> options) : this(next, hostingEnv, HtmlEncoder.Default, options) { } public DirectoryBrowserMiddleware(RequestDelegate next, IWebHostEnvironment hostingEnv, HtmlEncoder encoder, IOptions<DirectoryBrowserOptions> options) { _next = next; _options = options.Value; // If no FileProvider is specified, hostingEnv.WebRootFileProvider is used by default _fileProvider = _options.FileProvider ?? Helpers.ResolveFileProvider(hostingEnv); _formatter = _options.Formatter ?? new HtmlDirectoryFormatter(encoder); _matchUrl = _options.RequestPath; } public Task Invoke(HttpContext context) { // If the Endpoint has been matched, skip // If the HTTP request method is neither Get nor Head, skip // If the request paths do not match, skip // Skip if file directory does not exist 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; } // Generate file browse view 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
The DefaultFilesMiddleware can be easily registered through the UseDefaultFiles extension method:
public static class DefaultFilesExtensions { public static IApplicationBuilder UseDefaultFiles(this IApplicationBuilder app) { return app.UseMiddleware<DefaultFilesMiddleware>(); } 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<DefaultFilesMiddleware>(Options.Create(options)); } }
Next, check the Invoke method of DefaultFilesMiddleware:
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<DefaultFilesOptions> options) { _next = next; _options = options.Value; // If no FileProvider is specified, hostingEnv.WebRootFileProvider is used by default _fileProvider = _options.FileProvider ?? Helpers.ResolveFileProvider(hostingEnv); _matchUrl = _options.RequestPath; } public Task Invoke(HttpContext context) { // If the Endpoint has been matched, skip // If the HTTP request method is neither Get nor Head, skip // If the request paths do not match, skip 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 < _options.DefaultFileNames.Count; matchIndex++) { string defaultFile = _options.DefaultFileNames[matchIndex]; var file = _fileProvider.GetFileInfo(subpath.Value + defaultFile); // Default page found if (file.Exists) { if (_options.RedirectToAppendTrailingSlash && !Helpers.PathEndsInSlash(context.Request.Path)) { Helpers.RedirectToPathWithSlash(context); return Task.CompletedTask; } // Rewrite to the Url of the default page, and then provide the page through StaticFileMiddleware context.Request.Path = new PathString(Helpers.GetPathValueWithSlash(context.Request.Path) + defaultFile); break; } } } } return _next(context); } }
FileServer
FileServer is not a specific middleware. Its implementation still depends on three middleware: static filemiddleware, DirectoryBrowserMiddleware and DefaultFilesMiddleware. However, we can look at the logic in 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) { // Enable default page if (options.EnableDefaultFiles) { app.UseDefaultFiles(options.DefaultFilesOptions); } // Enable directory browsing if (options.EnableDirectoryBrowsing) { app.UseDirectoryBrowser(options.DirectoryBrowserOptions); } return app.UseStaticFiles(options.StaticFileOptions); } }
FileProvider in IWebHostingEnvironment
The interface IHostingEnvironment contains two file providers: ContentRootFileProvider and WebRootFileProvider. Let's take a look at how they are initialized.
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(), }; // Here's the point. Look at the Initialize method 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; // Initialize ContentRootFileProvider hostingEnvironment.ContentRootFileProvider = new PhysicalFileProvider(hostingEnvironment.ContentRootPath); var webRoot = options.WebRoot; if (webRoot == null) { // If the / wwwroot directory exists, it is set to the Web root directory 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); } // Initialize WebRootFileProvider hostingEnvironment.WebRootFileProvider = new PhysicalFileProvider(hostingEnvironment.WebRootPath); } else { hostingEnvironment.WebRootFileProvider = new NullFileProvider(); } hostingEnvironment.EnvironmentName = options.Environment ?? hostingEnvironment.EnvironmentName; } }
be careful
- When using UseDirectoryBrowser and usestatic files to provide file browsing and access, URL s are limited by case and underlying file system characters. For example, Windows is not case sensitive, but Mac OS and Linux are case sensitive.
- If IIS is used to host applications, the static file processor provided with IIS does not work and is processed using the ASP.NET Core Module, including static file processing.
Summary
- Use the UseFileServer extension method to provide file browsing and access, which integrates the functions of three middleware: usestatic files, UseDirectoryBrowser and UseDefaultFiles.
- UseStaticFiles: register StaticFilesMiddleware to provide file access
- UseDirectoryBrowser: register DirectoryBrowserMiddleware and provide file directory browsing
- UseDefaultFiles: register DefaultFilesMiddleware. When the Url does not specify the file name to be accessed, the default page is provided.
- All file providers implement the interface IFileProvider. There are three common file providers:
- PhysicalFileProvider: provides access to the physical file system
- ManifestEmbeddedFileProvider: provides access to files embedded in an assembly
- CompositeFileProvider: used to integrate multiple file providers.
- ContentRootFileProvider (the default directory is the project root directory) and WebRootFileProvider (the default directory is the Web root directory) can be obtained through IWebHostingEnvironment.