针对静态文件的请求是通过一个名为StaticFileMiddleware的中间件来实现的,这个中间件类型定义在NuGet包“Microsoft.AspNetCore.StaticFiles”中,所以我们需要预先按照这个NuGet包。整个应用只包含如下所示的这几行代码,StaticFileMiddleware这个中间件的注册是通过调用ApplicationBuilder的扩展方法UseStaticFiles来完成的。
public class Program{ public static void Main(){ new WebHostBuilder().UseContentRoot(Directory.GetCurrentDirectory()).UseKestrel() .Configure(app => app.UseStaticFiles()).Build() .Run();}}除了注册必需的StaticFileMiddleware中间件之外,我们还调用了WebHostBuilder的UseContentRoot方法将当前项目的根目录作为ContentRoot目录。我们知道ASP.NET Core应用具有两个重要的根目录,它们分别是ContentRoot和WebRoot,后者也是对外发布的静态文件默认使用的根目录。由于WebRoot目录的默认路径就是“{contentroot}/wwwroot”,所示上面这段程序就是将项目中的这个wwwroot目录下的所有静态文件发布出来。
上面我们通过一个简单的实例将WebRoot所在目录下的所有静态文件直接发布出来。如果我们需要发布的静态文件存储在其他目录下呢?依旧是演示的这个应用,现在我们将一些文档存储在如下图所示的“~/doc/”目录下并以Web的形式发布出来,我们的程序又该如何编写呢?
我们知道ASP.NET Core应用大部分情况下都是利用一个FileProvider对象来读取文件的,它在处理针对静态文件的请求是也不例外。对于我们调用ApplicationBuilder的扩展方法UseStaticFiles方法注册的这个类型为StaticFileMiddleware的中间件,其内部具有一个FileProvider和请求路径的映射关系。如果调用UseStaticFiles方法没有指定任何的参数,那么这个映射关系的请求路径就是应用的基地址(PathBase),而FileProvider自然就是指向WebRoot目录的PhysicalFileProvider。
上述的这个需求可以通过显式注册这个映射的方式来实现,为此我们在现有程序的基础上额外添加了一次针对UseStaticFiles方法的调用,并通过指定的参数(是一个StaticFileOptions对象)显式指定了采用的FileProvider(针对“~/doc/”的PhysicalFileProvider)和请求路径(“/documents”)。
public class Program{public static void Main(){ string contentRoot = Directory.GetCurrentDirectory();new WebHostBuilder().UseContentRoot(contentRoot).UseKestrel() .Configure(app => app .UseStaticFiles() .UseStaticFiles(new StaticFileOptions {FileProvider = new PhysicalFileProvider(Path.Combine(contentRoot, "doc")),RequestPath = "/documents" })).Build() .Run();}}按照上面这段程序指定的映射关系,对于存储在“~/doc/”目录下的这个PDF文件(“checklist.pdf”),发布在Web上的URL为“http://localhost:5000/documents/checklist.pdf”。当我们在浏览器上请求这个地址时,该PDF文件的内容将会按照如下图所示的形式显示在浏览器上。
二、浏览目录内容
注册的StaticFileMiddleware中间件只会处理针对某个具体静态文件的额请求,如果我们向针对某个目录的URL发送HTTP请求(比如“http://localhost:5000/img/”),得到的将是一个状态为404的响应。不过我们可以通过注册另一个名为DirectoryBrowserMiddleware的中间件来显示请求目录的内容。具体来说,这个中间件会返回一个HTML页面,请求目录下的所有文件将以表格的形式包含在这个页面中。对于我们演示的这个应用来说,我们可以按照如下的方式调用UseDirectoryBrowser方法来注册这个DirectoryBrowserMiddleware中间件。
public class Program{ public static void Main(){string contentRoot = Directory.GetCurrentDirectory();IFileProvider fileProvider = new PhysicalFileProvider(Path.Combine(contentRoot, "doc"));new WebHostBuilder() .UseContentRoot(contentRoot) .UseKestrel() .Configure(app => app .UseStaticFiles() .UseStaticFiles(new StaticFileOptions { FileProvider = fileProvider, RequestPath = "/documents"}) .UseDirectoryBrowser() .UseDirectoryBrowser(new DirectoryBrowserOptions { FileProvider = fileProvider, RequestPath = "/documents" })) .Build() .Run();}}当上面这个应用启动之后,如果我们利用浏览器向针对某个目录的URL(比如“http://localhost:5000/”或者“http://localhost:5000/img/”),目标目录的内容(包括子目录和文件)将会以下图所示的形式显示在一个表格中。不仅仅如此,子目录和文件均会显示为链接,指向目标目录或者文件的URL。
三、显示默认页面
从安全的角度来讲,利用注册的UseDirectoryBrowser中间件显示一个目录浏览页面会将整个目标目录的接口和所有文件全部暴露出来,所以这个中间件需要根据自身的安全策略谨慎使用。对于针对目录的请求,另一种更为常用的响应策略就是显示一个保存在这个目录下的默认页面。按照约定,作为默认页面的文件一般采用如下四种命名方式:default.htm、default.html、index.htm或者index.html。针对目标目录下默认页面的呈现实现在一个名为DefaultFilesMiddleware的中间件中,我们演示的这个应用可以按照如下的方式调用UseDefaultFiles方法来注册这个中间件。
public class Program{public static void Main(){string contentRoot = Directory.GetCurrentDirectory();IFileProvider fileProvider = new PhysicalFileProvider(Path.Combine(contentRoot, "doc")); new WebHostBuilder().UseContentRoot(contentRoot).UseKestrel().Configure(app => app.UseDefaultFiles().UseDefaultFiles(new DefaultFilesOptions{RequestPath = "/documents",FileProvider = fileProvider,}).UseStaticFiles() .UseStaticFiles(new StaticFileOptions{FileProvider = fileProvider,RequestPath = "/documents"}).UseDirectoryBrowser().UseDirectoryBrowser(new DirectoryBrowserOptions{ FileProvider = fileProvider, RequestPath = "/documents"})).Build().Run();}}现在我们在“~/wwwroot/img/”目录下创建一个名为index.htm的默认页面,现在利用浏览器访问这个目录对应的URL(“http://localhost:5000/img/”),显示就时这个页面的内容。
我们必须在注册StaticFileMiddleware和DirectoryBrowserMiddleware之前注册DefaultFilesMiddleware,否则它起不了任何作用。由于DirectoryBrowserMiddleware和DefaultFilesMiddleware这两个中间件处理的均是针对目录的请求,如果DirectoryBrowserMiddleware先被注册,那么显示的总是目录的内容。若DefaultFilesMiddleware先被注册,在默认页面不存在情况下回显示目录的内容。至于为什么要先于StaticFileMiddleware之前注册DefaultFilesMiddleware,则是因为后者是通过采用URL重写的方式实现的,也就是说这个中间件会将针对目录的请求改写成针对默认页面的请求,而最终针对默认页面的请求还得依赖StaticFileMiddleware完成。
DefaultFilesMiddleware中间件在默认情况下总是以约定的名称(default.htm、default.html、index.htm或者index.html)在当前请求的目录下定位默认页面。如果我们希望作为默认页面的文件不能按照这样的约定命名(比如readme.htm),我们需要按照如下的方式显式指定默认页面的文件名。
public class Program{public static void Main(){string contentRoot = Directory.GetCurrentDirectory();IFileProvider fileProvider = new PhysicalFileProvider(Path.Combine(contentRoot, "doc"));DefaultFilesOptions options1 = new DefaultFilesOptions();DefaultFilesOptions options2 = new DefaultFilesOptions{RequestPath = "/documents",FileProvider = fileProvider }; options1.DefaultFileNames.Add("readme.htm");options2.DefaultFileNames.Add("readme.htm");new WebHostBuilder().UseContentRoot(contentRoot).UseKestrel().Configure(app => app.UseDefaultFiles(options1).UseDefaultFiles(options2).UseStaticFiles().UseStaticFiles(new StaticFileOptions{FileProvider = fileProvider,RequestPath = "/documents"}).UseDirectoryBrowser().UseDirectoryBrowser(new DirectoryBrowserOptions{FileProvider = fileProvider,RequestPath = "/documents"})).Build().Run();}}四、映射媒体类型
public class Program{public static void Main(){ new WebHostBuilder().UseContentRoot(Directory.GetCurrentDirectory();).UseKestrel().Configure(app => app.UseStaticFiles(new StaticFileOptions {ServeUnknownFileTypes = true,DefaultContentType = "image/jpg"})).Build().Run();}}上述这种解决方案只能设置一种默认媒体类型,如果具有多种需要映射成不同媒体类型的非识别文件类型,采用这种方案就无能为力了,所以最根本的解决方案还是需要将不能识别的文件类型和对应的媒体类型进行映射。由于StaticFileMiddleware使用的ContentTypeProvider是可以定制的,我们可以按照如下的方式显式地为StaticFileMiddleware指定一个FileExtensionContentTypeProvider对象作为它的ContentTypeProvider,然后将取缺失的映射添加到这个FileExtensionContentTypeProvider对象上。
public class Program{public static void Main(){FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();contentTypeProvider.Mappings.Add(".img", "image/jpg"); new WebHostBuilder().UseContentRoot(Directory.GetCurrentDirectory()).UseKestrel().Configure(app => app.UseStaticFiles(new StaticFileOptions{ContentTypeProvider = contentTypeProvider})).Build().Run();}}以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,同时也希望多多支持脚本之家!