dotnet 核心 UseStaticFiles 索引回退

问题描述 投票:0回答:2

我的 Startup.cs 中有这个

Configure
方法。 它做了三件事:

  • 在 wwwroot 下提供静态文件
  • 为index.html添加CSP标头
  • 通过 /settings.json 路由提供参数
     public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
         if (env.IsDevelopment())
         {
            app.UseDeveloperExceptionPage();
         }

         app.UseHttpsRedirection();

         // Defaults to index.html
         var defaultFilesOptions = new DefaultFilesOptions();
         defaultFilesOptions.DefaultFileNames.Clear();
         defaultFilesOptions.DefaultFileNames.Add("index.html");
         app.UseDefaultFiles(defaultFilesOptions);

         var staticFileOptions = new StaticFileOptions
         {
            OnPrepareResponse = ctx =>
            {
               // Add CSP for index.html
               if (ctx.File.Name == "index.html")
               {
                  ctx.Context.Response.Headers.Append(
                     "Content-Security-Policy", "default-src 'self'" // etc
                  );
               }
            }
         };

         app.UseStaticFiles(staticFileOptions); // wwwroot

         app.UseRouting();

         app.UseEndpoints(endpoints =>
         {
            // Settings.json endpoint
            endpoints.MapGet("/settings.json", async context =>
            {
               string json = $@"
                {{
                   ""myConfig"": ""{_configuration["myParameter"]}""
                }}";
               await context.Response.WriteAsync(json);
            });
         });
      }
   }

wwwroot下的文件实际上是一个带有路由的vue.js应用程序。我需要为所有不存在的请求返回

index.html
,以便客户端路由控制页面。

目前它返回404并且没有传入

OnPrepareResponse
钩子。

如何配置索引回退以使路由器在历史模式下工作? 我认为可以通过 web.config 中的配置来实现,但我更喜欢在 Startup.js 中配置它,所以这一切都在同一个地方。

.net-core static-files
2个回答
2
投票

我最终编写了一个执行索引回退的文件提供程序。它封装了一个

PhysicalFileProvider
,如果没有找到文件,在一定条件下返回
index.html
。在我的例子中,条件基于文件夹 css、img 或 js。

它是这样实现的:

using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using System.Linq;

public class IndexFallbackFileProvider : IFileProvider
{
   private readonly PhysicalFileProvider _innerProvider;

   public IndexFallbackFileProvider(PhysicalFileProvider physicalFileProvider)
   {
      _innerProvider = physicalFileProvider;    
   }

   public IDirectoryContents GetDirectoryContents(string subpath)
   {
      return _innerProvider.GetDirectoryContents(subpath);
   }

   public IFileInfo GetFileInfo(string subpath)
   {
      var fileInfo = _innerProvider.GetFileInfo(subpath);
      if(!fileInfo.Exists && MustFallbackToIndex(subpath))
      {
         if(!_staticFilesFolders.Any(f => subpath.Contains(f)))
         {
            fileInfo = _innerProvider.GetFileInfo("/index.html");
         }         
      }

      return fileInfo;
   }

   // Plain 404 are OK for css, img, js.
   private static string[] _staticFilesFolders = new string[] { "/css/", "/img/", "/js/" };
   private static bool MustFallbackToIndex(string subpath)
   {
      return !_staticFilesFolders.Any(f => subpath.Contains(f));
   }

   public IChangeToken Watch(string filter)
   {
      return _innerProvider.Watch(filter);
   }
}

然后,在startup.config中,我使用这个提供程序。 另外,我必须将

ServeUnknownFileTypes
设置为
true
才能响应对
/path/without/extension
的请求。

var physicalFileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "wwwroot"));
var fileProvider = new IndexFallbackFileProvider(physicalFileProvider);

var staticFileOptions = new StaticFileOptions
{
   FileProvider = fileProvider,
   ServeUnknownFileTypes = true
};

app.UseStaticFiles(staticFileOptions);

0
投票

我喜欢@Johnny5的解决方案,但想返回index.html文件和任何JS或CSS文件,即使重新加载页面时基目录是错误的。

通过一次删除一个目录的前缀,我们可以找到这些文件,即使它们位于像

/assets/index-2562.js
这样的路径上。

using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using System.Linq;

/// <summary>
/// Strips off any directories until it finds a match
/// This allows an SPA to work even after page reload with some SPA path still on the URL
/// </summary>
public class IndexFallbackFileProvider : IFileProvider
{
    private readonly PhysicalFileProvider innerProvider;

    public IndexFallbackFileProvider(PhysicalFileProvider physicalFileProvider)
    {
        innerProvider = physicalFileProvider;
    }

    // For an SPA we may be called with a path like /foo/bar left over from some earlier SPA navigation
    // We need to remove these prefix directories until we find a match

    public IDirectoryContents GetDirectoryContents(string path)
    {
        string subpath = path.TrimStart(System.IO.Path.DirectorySeparatorChar);  // remove leading /
        while (true)
        {
            var result = innerProvider.GetDirectoryContents(subpath);
            if (result.Exists) return result;

            string? directory = System.IO.Path.GetDirectoryName(subpath);
            if (directory is null) break;

            int index = directory.IndexOf(System.IO.Path.DirectorySeparatorChar);
            if (index < 0) break;

            subpath = subpath.Substring(index + 1);
        }
        return innerProvider.GetDirectoryContents("");
    }

    public IFileInfo GetFileInfo(string path)
    {
        string subpath = path.TrimStart(System.IO.Path.DirectorySeparatorChar);  // remove leading /
        while (true)
        {
            var result = innerProvider.GetFileInfo(subpath);
            if (result.Exists) return result;

            string? directory = System.IO.Path.GetDirectoryName(subpath);
            if (directory is null) break;

            int index = directory.IndexOf(System.IO.Path.DirectorySeparatorChar);
            if (index < 0) break;

            subpath = subpath.Substring(index + 1);
        }
        return innerProvider.GetFileInfo("index.html");
    }

    public IChangeToken Watch(string filter)
    {
        return innerProvider.Watch(filter);
    }
}

相同的启动代码:

var physicalFileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "wwwroot"));
var fileProvider = new IndexFallbackFileProvider(physicalFileProvider);

var staticFileOptions = new StaticFileOptions
{
   FileProvider = fileProvider,
   ServeUnknownFileTypes = true
};

app.UseStaticFiles(staticFileOptions);
© www.soinside.com 2019 - 2024. All rights reserved.