ASP.NET Core静态文件中间件[1]: 搭建文件服务器
虽然ASP.NET Core是一款“动态”的Web服务端框架,但是由它接收并处理的大部分是针对静态文件的请求,最常见的是开发Web站点使用的3种静态文件(JavaScript脚本、CSS样式和图片)。ASP.NET Core提供了3个中间件来处理针对静态文件的请求,利用它们不仅可以将物理文件发布为可以通过HTTP请求获取的Web资源,还可以将所在的物理目录的结构呈现出来。通过HTTP请求获取的Web资源大部分来源于存储在服务器磁盘上的静态文件。对于ASP.NET Core应用来说,如果将静态文件存储到约定的目录下,绝大部分文件类型都是可以通过Web的形式对外发布的。基于静态文件的请求由3个中间件负责处理,它们均定义在NuGet包“Microsoft.AspNetCore.StaticFiles”中,利用这3个中间件完全可以搭建一个基于Web的文件服务器,下面做相关的实例演示。
目录
一、发布物理文件
二、呈现目录结构
三、显示默认页面
四、映射媒体类型
一、发布物理文件
我们创建的演示实例是一个简单的ASP.NET Core应用,它的项目结构如下图所示。在默认作为WebRoot的“wwwroot”目录下,可以将JavaScript脚本文件、CSS样式文件和图片文件存放到对应的子目录(js、css和img)下。WebRoot目录下的所有文件将自动发布为Web资源,客户端可以访问相应的URL来读取对应文件的内容。

针对具体某个静态文件的请求是通过一个名为StaticFileMiddleware的中间件来处理的。如下面的代码片段所示,承载ASP.NET Core应用的程序中调用IApplicationBuilder接口的UseStaticFiles扩展方法注册的就是这样一个中间件。
public class Program
{
public static void Main()
{
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder=>builder.Configure(app => app.UseStaticFiles()))
.Build()
.Run();
}
}
上述程序运行之后,就可以通过GET请求的方式来读取对应文件的内容。请求采用的URL由目标文件的路径决定。具体来说,目标文件相对于WebRoot目录的路径就是对应URL的路径,如JPG图片文件“~/wwwroot/img/dolphin1.jpg”对应的URL路径为“/img/dolphin1.jpg”。如果直接利用浏览器访问这个URL,目标图片就会直接以下图所示的形式显示出来。

上面通过一个简单的实例将WebRoot所在目录下的所有静态文件发布为Web资源,如果需要发布的静态文件存储在其他目录下呢?下面将上面演示的应用程序的一些文档存储在下图所示的“~/doc/”目录下,那么对应的程序又该如何编写?

ASP.NET Core应用在大部分情况下都是利用一个IFileProvider对象来读取文件的针对静态文件的读取请求也不例外。对于IApplicationBuilder接口的UseStaticFiles扩展方法注册的StaticFileMiddleware中间件来说,它的内部维护着一个IFileProvider对象和请求路径的映射关系。如果调用UseStaticFiles方法没有指定任何参数,那么这个映射关系的请求路径就是应用的基地址(PathBase),对应的IFileProvider对象自然就是指向WebRoot目录的PhysicalFileProvider对象。
上述需求可以通过显式定制这个映射关系的方式来实现。如下面的代码片段所示,我们在现有程序的基础上额外添加了一次针对UseStaticFiles扩展方法的调用,在本次调用中指定一个对应的Options对象(一个类型为StaticFileOptions的对象)作为参数来定制请求路径(“/documents”)与对应IFileProvider对象(针对路径“~/doc/”的PhysicalFileProvider对象)之间的映射关系。
public class Program
{
public static void Main()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var options = new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(path),
RequestPath = "/documents"
};
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app
.UseStaticFiles()
.UseStaticFiles(options)))
.Build()
.Run();
}
}
按照上面这段程序指定的映射关系,对于存储在“~/doc/”目录下的这个PDF文件(checklist.pdf),对应URL的路径就应该是“/documents/checklist.pdf”。如果利用浏览器请求这个地址时,PDF文件的内容就会按照下图所示的形式显示在浏览器上。

二、呈现目录结构
上面的演示实例注册的StaticFileMiddleware中间件只会处理针对具体的某个静态文件的请求,如果利用浏览器发送一个针对目录的请求(如“http://localhost:5000/img/”),得到的将是一个状态为“404 Not Found”的响应。如果希望浏览器呈现出目标目录的结构,就可以注册另一个名为DirectoryBrowserMiddleware的中间件。这个中间件会返回一个HTML页面,请求目录下的结构会以表格的形式显示在这个页面中。我们演示的程序可以按照如下方式调用IApplicationBuilder接口的UseDirectoryBrowser扩展方法来注册DirectoryBrowserMiddleware中间件。
public class Program
{
public static void Main()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var fileProvider = new PhysicalFileProvider(path); var fileOptions = new StaticFileOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
}; var diretoryOptions = new DirectoryBrowserOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
}; Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app
.UseStaticFiles()
.UseStaticFiles(fileOptions)
.UseDirectoryBrowser()
.UseDirectoryBrowser(diretoryOptions)))
.Build()
.Run();
}
}
当上面的应用启动之后,如果利用浏览器向针对某个目录的URL(如“http://localhost:5000/”或者“http://localhost:5000/img/”)发起请求,目标目录的内容(包括子目录和文件)就会以图14-5所示的形式显示在一个表格中。可以看出,在呈现的表格中,当前目录的子目录和文件均会显示为链接。

三、显示默认页面
从安全的角度来讲,利用注册的UseDirectoryBrowser中间件会将整个目标目录的结构和所有文件全部暴露出来,所以这个中间件需要根据自身的安全策略谨慎使用。对于针对目录的请求,更加常用的处理策略就是显示一个保存在这个目录下的默认页面。默认页面文件一般采用如下4种命名约定:default.htm、default.html、index.htm和index.html。针对默认页面的呈现实现在一个名为DefaultFilesMiddleware的中间件中,我们演示的这个应用就可以按照如下方式调用IApplicationBuilder接口的UseDefaultFiles扩展方法来注册这个中间件。
public class Program
{
public static void Main()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var fileProvider = new PhysicalFileProvider(path); var fileOptions = new StaticFileOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var diretoryOptions = new DirectoryBrowserOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var defaultOptions = new DefaultFilesOptions
{
RequestPath = "/documents",
FileProvider = fileProvider,
}; Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app
.UseDefaultFiles()
.UseDefaultFiles(defaultOptions)
.UseStaticFiles()
.UseStaticFiles(fileOptions)
.UseDirectoryBrowser()
.UseDirectoryBrowser(diretoryOptions)))
.Build()
.Run();
}
}
下面在“~/wwwroot/img/”目录和“~/doc”目录下分别创建一个名为index.html的默认页面,并且在该.html文件的主体部分指定一段简短的文字(This is an index page!)。在应用启动之后,可以利用浏览器访问这两个目录对应的URL(“http://localhost:5000/img/”和“http://localhost:5000/documents/”),下图显示的就是这个默认页面的内容。

必须在注册StaticFileMiddleware中间件和DirectoryBrowserMiddleware中间件之前注册DefaultFilesMiddleware中间件,否则它无法发挥作用。这是因为DirectoryBrowserMiddleware中间件和DefaultFilesMiddleware中间件处理的均是针对目录的请求,如果先注册DirectoryBrowserMiddleware中间件,那么显示的总是目录的结构;如果先注册用于显示默认页面的DefaultFilesMiddleware中间件,那么在默认页面不存在的情况下它会将请求分发给后续中间件,而DirectoryBrowserMiddleware中间件会接收请求的处理并将当前目录的结构呈现出来。
要先于StaticFileMiddleware中间件之前注册DefaultFilesMiddleware中间件是因为后者是通过采用URL重写的方式实现的,也就是说,这个中间件会将针对目录的请求改写成针对默认页面的请求,而最终针对默认页面的请求还需要依赖StaticFileMiddleware中间件来完成。DefaultFilesMiddleware中间件在默认情况下总是以约定的名称(default.htm、default.html、index.htm和index.html)在当前请求的目录下定位默认页面。如果作为默认页面的文件没有采用这样的约定命名(如我们将默认页面命名为readme.html),就需要按照如下方式显式指定默认页面的文件名。
public class Program
{
public static void Main()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "doc");
var fileProvider = new PhysicalFileProvider(path);
var fileOptions = new StaticFileOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var diretoryOptions = new DirectoryBrowserOptions
{
FileProvider = fileProvider,
RequestPath = "/documents"
};
var defaultOptions1 = new DefaultFilesOptions();
var defaultOptions2 = new DefaultFilesOptions
{
RequestPath = "/documents",
FileProvider = fileProvider,
}; defaultOptions1.DefaultFileNames.Add("readme.html");
defaultOptions2.DefaultFileNames.Add("readme.html"); Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app
.UseDefaultFiles(defaultOptions1)
.UseDefaultFiles(defaultOptions2)
.UseStaticFiles()
.UseStaticFiles(fileOptions)
.UseDirectoryBrowser()
.UseDirectoryBrowser(diretoryOptions)))
.Build()
.Run();
}
}
四、映射媒体类型
通过上面演示的实例可以看出,浏览器能够准确地将请求的目标文件的内容正常呈现出来。对HTTP协议具有基本了解的读者应该都知道:响应文件能够在浏览器上被正常显示的基本前提是响应报文通过Content-Type报头携带的媒体类型必须与内容一致。我们的实例演示了针对两种文件类型的请求,一种是JPG文件,另一种是PDF文件,对应的媒体类型分别是image/jpg和application/pdf,那么用来处理静态文件请求的StaticFileMiddleware中间件是如何解析出对应的媒体类型的?
StaticFileMiddleware中间件针对媒体类型的解析是通过一个IContentTypeProvider对象来完成的,默认采用的是该接口的实现类型FileExtensionContentTypeProvider。顾名思义,FileExtensionContentTypeProvider根据文件的扩展命名来解析媒体类型。FileExtensionContentTypeProvider内部预定了数百种常用文件扩展名与对应媒体类型之间的映射关系,所以如果发布的静态文件具有标准的扩展名,那么StaticFileMiddleware中间件就能为对应的响应赋予正确的媒体类型。
如果某个文件的扩展名没有在预定义的映射之中,或者需要某个预定义的扩展名匹配不同的媒体类型,那么应该如何解决?同样是针对我们演示的这个实例,笔者将~/wwwroot/img/ dolphin1.jpg文件的扩展名改成.img,毫无疑问,StaticFileMiddleware中间件将无法为针对该文件的请求解析出正确的媒体类型。这个问题具有若干不同的解决方案,第一种方案就是按照如下方式让StaticFileMiddleware中间件支持不能识别的文件类型,并为它们设置一个默认的媒体类型。
public class Program
{
public static void Main()
{
var options = new StaticFileOptions
{
ServeUnknownFileTypes = true,
DefaultContentType = "image/jpg"
}; Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app.UseStaticFiles(options)))
.Build()
.Run();
}
}
上述解决方案只能设置一种默认媒体类型,如果具有多种需要映射成不同媒体类型的文件类型,采用这种方案就达不到目的,所以最根本的解决方案还是需要将不能识别的文件类型和对应的媒体类型进行映射。由于StaticFileMiddleware中间件使用的IContentTypeProvider对象是可以定制的,所以可以按照如下方式显式地为该中间件指定一个FileExtensionContentTypeProvider对象,然后将缺失的映射添加到这个对象上。
public class Program
{
public static void Main()
{
var contentTypeProvider = new FileExtensionContentTypeProvider();
contentTypeProvider.Mappings.Add(".img", "image/jpg");
var options = new StaticFileOptions
{
ContentTypeProvider = contentTypeProvider
};
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder => builder.Configure(app => app.UseStaticFiles(options)))
.Build()
.Run();
}
}
ASP.NET Core静态文件中间件[1]: 搭建文件服务器
ASP.NET Core静态文件中间件[2]: 条件请求以提升性能
ASP.NET Core静态文件中间件[3]: 区间请求以提供部分内容
ASP.NET Core静态文件中间件[4]: StaticFileMiddleware
ASP.NET Core静态文件中间件[5]: DirectoryBrowserMiddleware & DefaultFilesMiddleware
ASP.NET Core静态文件中间件[1]: 搭建文件服务器的更多相关文章
- ASP.NET Core 静态文件 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 静态文件 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 静态文件 前几章节中,我们学习了 ASP.NET Core 的中间件 ...
- ASP.NET Core 静态文件
静态文件(HTML,CSS,图片和Javascript之类的资源)会被ASP.NET Core应用直接提供给客户端. 静态文件通常位于网站根目录(web root) <content-root& ...
- 细说ASP.NET Core静态文件的缓存方式
一.前言 我们在优化Web服务的时候,对于静态的资源文件,通常都是通过客户端缓存.服务器缓存.CDN缓存,这三种方式来缓解客户端对于Web服务器的连接请求压力的. 本文指在这三个方面,在ASP.NET ...
- ASP.NET Core静态文件处理源码探究
前言 静态文件(如 HTML.CSS.图像和 JavaScript)等是Web程序的重要组成部分.传统的ASP.NET项目一般都是部署在IIS上,IIS是一个功能非常强大的服务器平台,可以直接 ...
- ASP.NET Core 静态文件及JS包管理器(npm, Bower)的使用
在 ASP.NET Core 中添加静态文件 虽然ASP.NET主要大都做着后端的事情,但前端的一些静态文件也是很重要的.在ASP.NET Core中要启用静态文件,需要Microsoft.AspNe ...
- .Net Core静态文件中间件StaticFiles的使用
以前,当我们的网站需要显示图片的时候,直接在网站目录下新建文件夹,把图片放在这个文件夹下,然后通过文件夹的路径就可以访问到. 但是在.net core中不可以这样,要通过中间件StaticFiles配 ...
- asp .net core 静态文件资源
前言 对静态资源的简单的一个概况,在<重新整理.net core 计1400篇>系列后面会深入. 正文 我们在加入中间件是这样写的: app.UseStaticFiles(); 默认是给w ...
- 《ASP.NET Core 高性能系列》静态文件中间件
一.概述 静态文件(如 HTML.CSS.图片和 JavaScript等文件)是 Web程序直接提供给客户端的直接加载的文件. 较比于程序动态交互的代码而言,其实原理都一样(走Http协议), ASP ...
- 15.ASP.NET Core 应用程序中的静态文件中间件
在这篇文章中,我将向大家介绍,如何使用中间件组件来处理静态文件.这篇文章中,我们讨论下面几个问题: 在ASP.NET Core中,我们需要把静态文件存放在哪里? 在ASP.NET Core中 wwwr ...
随机推荐
- js常用函数和事件
1.常规函数 javascript常规函数包括以下9个函数: (1)alert函数:显示一个警告对话框,包括一个OK按钮. (2)confirm函数:显示一个确认对话框,包括OK.Cancel按钮. ...
- 新同事不讲“码”德,这SQL写得太野了,请耗子尾汁~
今天来分享几个MySQL常见的SQL错误(不当)用法.我们在作为一个初学者时,很有可能自己在写SQL时也没有注意到这些问题,导致写出来的SQL语句效率低下,所以我们也可以自省自检一下. 1. LIMI ...
- rkhunter
0 0 * * 0 /usr/local/bin/rkhunter -c --cronjob 定时工具
- 精尽MyBatis源码分析 - MyBatis 的 SQL 执行过程(一)之 Executor
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- Vegas技巧分享,如何实现5.1立体声道
Vegas Pro 15除了有各种好用的功能之外,还可以使用此软件制作5.1立体声道的音乐.如果你家里有多个音箱,那么你再也不用担心找不到5.1声道的音乐了,Vegas完全可以帮你实现. 打开简体中文 ...
- PHP 统计目录下文件数和文件大小
1 /** 2 * 统计文件数和文件大小 3 */ 4 private function getFileCacheCount($pathName) 5 { 6 $data = [ 7 'num' =& ...
- wirshark找不到本地接口
解决方法: 1.以管理员权限运行wireshark 2.以管理员身份运行cmd,输入net start npf,打开网络抓包服务,运行wireshark legacy,选择要抓包的网卡
- Spring Cloud 学习 (九) Spring Security, OAuth2
Spring Security Spring Security 是 Spring Resource 社区的一个安全组件.在安全方面,有两个主要的领域,一是"认证",即你是谁:二是& ...
- 安装pillow报错处理
sudo python3 pip install pillow 报错:The headers or library files could not be found for jpeg,... 解决:安 ...
- 微软发布 Pylance:改善 VS Code 中的 Python 体验
原标题:微软发布 Pylance:改善 VS Code 中的 Python 体验 来源:开源中国 微软宣布推出一种新的 Python 语言服务器,名为 Pylance,其可利用语言服务器协议与 VS ...