ASP.NET Core静态文件处理源码探究 (5)

很多时候出于安全考虑或者其他原因我们想在访问某个目录的时候返回一个默认的页面或展示,这个事实我们就需要使用UseDefaultFiles中间件,当我们配置了这个中间件,如果命中了配置路径,那么会直接返回默认的页面信息,简单使用方式如下

//wwwroot目录访问展示默认文件 app.UseDefaultFiles(); //或自定义目录默认展示文件 var fileProvider = new PhysicalFileProvider($"{env.ContentRootPath}/staticfiles"); app.UseDefaultFiles(new DefaultFilesOptions { RequestPath = "/staticfiles", FileProvider = fileProvider });

老规矩,我们查看下注册UseDefaultFiles的源码[点击查看DefaultFilesExtensions源码]

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)); } }

使用方式和UseStaticFiles、UseDirectoryBrowser是一样,最终都是调用传递DefaultFilesOptions的方法,我们查看一下DefaultFilesOptions的大致实现[点击查看源码]

public class DefaultFilesOptions : SharedOptionsBase { public DefaultFilesOptions() : this(new SharedOptions()) { } public DefaultFilesOptions(SharedOptions sharedOptions) : base(sharedOptions) { //系统提供的默认页面的名称 DefaultFileNames = new List<string> { "default.htm", "default.html", "index.htm", "index.html", }; } /// <summary> /// 通过这个属性可以配置默认文件名称 /// </summary> public IList<string> DefaultFileNames { get; set; } }

和之前的方法如出一辙,都是继承自SharedOptionsBase,通过DefaultFileNames我们可以配置默认文件的名称,默认是default.html/htm和index.html/htm。我们直接查看中间件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; _fileProvider = _options.FileProvider ?? Helpers.ResolveFileProvider(hostingEnv); _matchUrl = _options.RequestPath; } public Task Invoke(HttpContext context) { //1.我们使用UseDefaultFiles中间件的时候要置于UseRouting之上,否则就会不生效 //2.IsGetOrHeadMethod判断请求为Get或Head的情况下才生效 //3.TryMatchPath判断请求的路径和设置的路径是否可以匹配的上 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); if (file.Exists) { //判断请求路径是否已"http://www.likecs.com/"结尾,如果不是则从定向(这个点个人感觉可以改进) if (!Helpers.PathEndsInSlash(context.Request.Path)) { context.Response.StatusCode = StatusCodes.Status301MovedPermanently; var request = context.Request; var redirect = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path + "http://www.likecs.com/", request.QueryString); context.Response.Headers[HeaderNames.Location] = redirect; return Task.CompletedTask; } //如果匹配的上,则将配置的启用默认文件的路径+遍历到的默认文件名称的路径组合成新的Path交给_next(context) //比如将组成类似这种路径/staticfiles/index.html向下传递 context.Request.Path = new PathString(context.Request.Path.Value + defaultFile); break; } } } } return _next(context); } }

这个中间件的实现思路也非常简单主要的工作就是,匹配配置的启用默认文件的路径+遍历到的默认文件名称的路径是否存在,如果匹配的上,则将配置的启用默认文件的路径+遍历到的默认文件名称的路径组合成新的Path(比如/staticfiles/index.html)交给后续的中间件去处理。这里值得注意的是UseDefaultFiles 必须要配合UseStaticFiles一起使用,而且注册位置要出现在UseStaticFiles之上。这也是为什么UseDefaultFiles只需要匹配到默认文件所在的路径并重新赋值给context.Request.Path既可的原因。
当然我们也可以自定义默认文件的名称,因为只要能匹配的到具体的文件既可

var defaultFilesOptions = new DefaultFilesOptions { RequestPath = "/staticfiles", FileProvider = fileProvider }; //我们可以清除掉系统默认的默认文件名称 defaultFilesOptions.DefaultFileNames.Clear(); defaultFilesOptions.DefaultFileNames.Add("mydefault.html"); app.UseDefaultFiles(defaultFilesOptions); 总结

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpypzs.html