DefaultFilesMiddleware中间件的目的在于将目标目录下的默认文件作为响应内容。我们知道,如果直接请求的就是这个默认文件,那么前面介绍的StaticFileMiddleware中间件会将这个文件响应给客户端。如果我们能够将针对目录的请求重定向到这个默认文件上,一切就迎刃而解了。实际上DefaultFilesMiddleware中间件的实现逻辑很简单,它采用URL重写的形式修改了当前请求的地址,即将针对目录的URL修改成针对默认文件的URL。
我们照例先来看看DefaultFilesMiddleware类型的定义。和其他两个中间件类似,DefaultFilesMiddleware的构造就有一个IOptions<DefaultFilesOptions>类型的参数来指定相关的配置选项。由于DefaultFilesMiddleware中间件本质上依然体现了请求路径与某个物理目录的映射,所以DefaultFilesOptions依然派生于SharedOptionsBase。DefaultFilesOptions的DefaultNames属性包含了预定义的默认文件名,我们可以看到它默认包含四个名称(default.htm、default.html、index.htm或者index.html)。
1: public class DefaultFilesMiddleware
2: {
3: public DefaultFilesMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, IOptions<DefaultFilesOptions> options);
4: public Task Invoke(HttpContext context);
5: }
6:
7: public class DefaultFilesOptions : SharedOptionsBase
8: {
9: public IList<string> DefaultFileNames { get; set; }
10:
11: public DefaultFilesOptions() : this(new SharedOptions()){}
12: public DefaultFilesOptions(SharedOptions sharedOptions) : base(sharedOptions)
13: {
14: this.DefaultFileNames = new List<string> { "default.htm", "default.html", "index.htm", "index.html" };
15: }
16: }
我们同样采用一种比较易于理解的形式重新定义这个DefaultFilesMiddleware类型以便于读者朋友理解它具体采用的请求处理逻辑。如下面的代码片段所示,DefaultFilesMiddleware和DirectoryBrowserMiddleware一样会对请求做相应的验证。如果当前目录下存在某个默认文件,那么它会将当前请求的URL修改成指向这个默认文件的URL。值得一提的是,DefaultFilesMiddleware中间件要求访问目录的请求路劲必须以字符“/”作为后缀,否则会在目前的路径上添加这个后缀并针对最终的路径发送一个重定向。
1: public class DefaultFilesMiddleware
2: {
3: private RequestDelegate _next;
4: private DefaultFilesOptions _options;
5:
6: public DefaultFilesMiddleware(RequestDelegate next, IHostingEnvironment env, IOptions<DefaultFilesOptions> options)
7: {
8: _next = next;
9: _options = options.Value;
10: _options.FileProvider = _options.FileProvider ?? env.WebRootFileProvider;
11: }
12:
13: public async Task Invoke(HttpContext context)
14: {
15: //只处理GET和HEAD请求
16: if (!new string[] { "GET", "HEAD" }.Contains(context.Request.Method,StringComparer.OrdinalIgnoreCase))
17: {
18: await _next(context);
19: return;
20: }
21:
22: //检验当前路径是否与注册的请求路径相匹配
23: PathString path = new PathString(context.Request.Path.Value.TrimEnd('/') + "/");
24: PathString subpath;
25: if (!path.StartsWithSegments(_options.RequestPath, out subpath))
26: {
27: await _next(context);
28: return;
29: }
30:
31: //检验目标目录是否存在
32: if (!_options.FileProvider.GetDirectoryContents(subpath).Exists)
33: {
34: await _next(context);
35: return;
36: }
37:
38: //检验当前目录是否包含默认文件
39: foreach (var fileName in _options.DefaultFileNames)
40: {
41: if (_options.FileProvider.GetFileInfo($"{subpath}{fileName}").Exists)
42: {
43: //如果当前路径不以"/"作为后缀,会响应一个针对“标准”URL的重定向
44: if (!context.Request.Path.Value.EndsWith("/"))
45: {
46: context.Response.StatusCode = 302;
47: context.Response.GetTypedHeaders().Location = new Uri(path.Value + context.Request.QueryString);
48: return;
49: }
50: //将针对目录的URL更新为针对默认文件的URL
51: context.Request.Path = new PathString($"{context.Request.Path}{fileName}");
52: }
53: }
54: await _next(context);
55: }
56: }
正是因为DefaultFilesMiddleware中间件采用URL重写的方式来响应默认文件,所以它最终依赖StaticFileMiddleware中间件来响应默认文件,所以针对后者的注册时必须的。也正是这个原因,这个中间件需要优先注册以确保URL重写发生在StaticFileMiddleware响应文件之前。