1 Overview
ASP.NET core 中对静态文件的处理的中间件是 Microsoft.AspNetCore.StaticFiles
这个包。其中包含了如下的中间件:
DefaultFilesMiddleware
DirectoryBrowserMiddleware
StaticFileMiddleware
还有一个中间件 FileServerMiddleware
用来依次注入上述三种的中间件。
这些类型中间件的 Options(SharedOptions)
包含以下共有的属性
public PathString RequestPath {get; set;}
public IFileProvider FileProvider {get; set;}
public bool RedictToAppendTrailingSlash {get; set;}
2 DefaultFilesMiddleware
首先这个中间件的作用是什么呢?假设我们的 Asp.Net core
应用程序如下
毫无疑问,将会返回一个 Hello World
, 那么假设我们在 wwwroot
目录下面增加了一个 index.html
文件
并且在 Configure
方法中 app.Run
调用之前增加下面两个方法
app.UseDefaultFiles();app.UseStaticFiles();
那么在运行应用程序的时候,得到的结果是
发现前面增加 index.html
页面被返回。这就是 DefaultFiles
的功能,当访问的路径(目录)没有对应的文件的时候,将挑选出默认的页面返回。
接下来我们探索它是怎么实现的。 DefaultFileOptions
继承 SharedOptions
并包含了 DefaultFileNames
属性,该属性指定默认的文件名,初始化有四种:
- default.htm
- default.hmtl
- index.htm
- index.html
如果我们不指定 FileProvier
,那么将会以 wwwroot
目录,创建一个 PhysicalFileProvider
对象。那么这个中间件是如何工作的呢?
- 首先三个检查条件: 1)没有命中其他的 endpoint, 2) Get/Head 方法,3)请求的路径是否为
RequestPath
开头 - 通过
fileProvider
获得所在的目录信息 - 依次迭代
DefaultFileNames
,如果这个文件在目录下存在,则取回文件信息 - 将文件名字添加到原本的请求路径中,然后跳出迭代,交给下一个中间件处理。
对于直接使用 UseDefaultFiles
的扩展方法,它是返回的 wwwroot
目录代表的 IFileProivder
而 RequestPath
则默认是为空字符串,当访问 https://localhost:43323/
时候,由 DefaultFiles
中间件处理后,访问的请求路径就变成了 https://localhost:43323/index.html
,而这个交给 StaticFiles
处理了。
2 StaticFileMiddleware
我们希望当上面的请求到达的时候,服务器的中间件能够返回相应的 index.html
文件作为请求的响应。
StaticFileMiddleware
的配置条件增加了下面几个内容
IContentTypeProvider ContentTypeProvider
可以根据请求的路径确定 MIME类型, 默认是FileExtensionContentTypeProvider
, 它是从IIS
中获取的UnknownServiceType
如果未知类型,是否还要继续处理,默认是 falseDefaultContentType
如果ContentTypeProvider
没有找到,而且还要继续处理,则返回DefaultContentType
类型OnPrepareResponse
如果请求完成,是否继续要做一些其他事情的委托,默认不进行任何操作
这个中间件的处理流程是这样的
- 如果由 endpoint, 则跳过
- 如果请求方法不是 Get or Head, 跳过
- 如果请求路径不是
RequestPath
开头的,跳过 - 如果无法查询到 contentType, 跳过
- 然后处理真正的静态文件请求
请求的过程是交给 StaticFileContext
处理的,分为两步
LookupFileInfo
ServeStaticFile
LookupFileInfo
这个方法主要目的是根据文件的相对路径,通过 IFileProvider
得到文件基本信息
- 是否存在 (Exist)
- 文件大小 (_length)
- 上一次修改时间 (_lastModified)
_etag
ServeStaticFile
在处理静态文件处理之前,需要处理请求 Header
中特殊情况
If-Match
如果请求 Header
中包含了 If-Match
字段,表明只有请求的资源的 etag
和 If-Match
中内容有一项完全相同的时候,才能返回资源, 比如
If-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"
If-Match: "67ab43", "54ed21", "7892dd"
If-Match: *
If-None-Match
于 If-Match
相反的,只有 etag
不匹配的时候,才会返回请求的响应。
If-Modified-Since
请求的资源的只有在 If-Modified-Since
指定的时间之后修改,才会返回请求响应
If-Unmodified-Since
请求的资源只会在 If-Unmodified-Since
指定的时候之后没有修改,才会返回请求的响应
If-Range
HTTP 请求可以请求部分数据,所以 If-Range
用来判断是否符合 Range
请求。这个其中的条件分为两种
- 时间: 如果在给定的事件后修改,则不能进行进行
Range
请求 - ETag: 如果请求资源的实体
Etag
不匹配,则不能进行Range
请求
Range
如果 If-Range
条件满足,就需要解析 Headers
中 Range
指定的长度大小,比如说
Range: <unit>=<range-start>-
Range: <unit>=<range-start>-<range-end>
Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>
Range: <unit>=-<suffix-length>
注意 Asp.NET Core
不支持多个区间段的请求。
在获取上述的信息后,就开始处理请求
- 只处理需要处理的请求
- 如果是
Head
请求,直接返回200 OK
响应 - 如果是 Range 请求,在计算出需要处理的长度之后,返回响应的文件长度
- 否则返回文件的全部内容
3. DirectoryBrowserMiddleware
这个中间可以实现在浏览器中浏览文件夹的功能,如下图所示
DirectoryBrowserOptions
增加了一个属性
IDirectoryFormatter Formatter
该接口可以将渲染FileInfo
的集合,默人实现方式是HtmlDirectoryFormatter
该中间件处理比较简单,对于满足请求,通过 IFileProvder
获取这个目录下所有的 FileInfo
信息,然后交给 IDirectoryFormatter
接口进行渲染。默认的实现方式,就是渲染出一张表。