6

我有一个 ASP.NET Core 项目,其中包含wwwroot目录和bower_components目录中的静态文件。

我可以通过将其添加到我的Startup.cs班级来提供这些文件:

StaticFileOptions rootFileOptions = new StaticFileOptions();
rootFileOptions.OnPrepareResponse = staticFilesResponseHandler;
StaticFileOptions bowerFileOptions = new StaticFileOptions();
bowerFileOptions.OnPrepareResponse = staticFilesResponseHandler;
string bowerDirectory = Path.Combine(Directory.GetCurrentDirectory(), "bower_components");
PhysicalFileProvider bowerPhysicalFileProvider = new PhysicalFileProvider(bowerDirectory);
bowerFileOptions.FileProvider = bowerPhysicalFileProvider;
bowerFileOptions.RequestPath = new PathString("/bower");
app.UseStaticFiles(rootFileOptions);
app.UseStaticFiles(bowerFileOptions);

然后从我的观点中引用它们如下:

<script type="text/javascript" src="/bower/jquery/dist/jquery.min.js" asp-append-version="true"></script>
<script type="text/javascript" src="/Libs/jQuery-UI/jquery-ui.min.js" asp-append-version="true"></script>

尽管asp-append-version对于位于下的资源似乎工作得很好wwwroot,但对于wwwroot. 不过,所有资源都得到了适当的服务;没有404或任何东西。上述代码生成的 HTML 如下:

<script type="text/javascript" src="/bower/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="/Libs/jQuery-UI/jquery-ui.min.js?v=YZKMNaPD9FY0wb12QiluqhIOWFhZXnjgiRJoxErwvwI"></script>

我究竟做错了什么?

4

3 回答 3

4

这个问题很老,但问题仍然存在,虽然 Artak 提供的解决方案有效,但在大多数情况下它在概念上是不正确的。首先让我们看看问题的根源:

asp-append-version查找IHostingEnvironment.WebRootFileProvider默认情况下使用PhysicalFileProvider指向该wwwroot文件夹的文件。

核心文档有一个关于如何在 web 根目录之外提供文件的示例:

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles(); // For the wwwroot folder

    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
        RequestPath = "/StaticFiles"
    });
}

wwwroot这允许您从和文件夹中提供静态文件MyStaticFiles。如果您有图像\MyStaticFiles\pic1.jpg,可以通过两种方式引用它:

<img src="~/pic1.jpg" />
<img src="~/StaticFiles/pic1.jpg" />

两者都将同样有效。这在概念上是不正确的,因为您为路径指定了别名/StaticFiles,因此不应将其文件与 root 组合/。但至少它有效,它给你你想要的。

可悲的是,asp-append-version不知道这一切。它应该,但它没有。应该是因为它旨在与静态文件(JavaScript、CSS 和图像)一起使用,所以如果我们更改配置以提供来自不同文件夹的静态文件,则asp-append-version可以获得这些配置的副本。它没有,所以我们需要通过 modify 单独配置它IHostingEnvironment.WebRootFileProvider

Artak 建议使用CompositeFileProvider它允许我们将多个文件提供程序分配给IHostingEnvironment.WebRootFileProvider. 这确实有效,但它有一个基本问题。CompositeFileProvider不允许我们RequestPathStaticFileOptions. 作为一种解决方法,Artak 建议我们不应该使用前缀,它利用了上述错误行为,即文件可以以两种方式引用。为了演示这个问题,假设另一个文件夹的结构如下:

|_ 我的静态文件
       |_ HTML
       | |_ 隐私.html
       | |_ 常见问题.html
       |_ 图片
         |_ 图像1.jpg

现在,文件MyStaticFiles\images夹中的所有文件会发生什么?假设它wwwroot也有images文件夹,它会工作还是给你两个同名文件夹的错误?文件~/images/image1.jpg将从哪里来?

无论它是否有效,通常有一个重要原因导致您将静态文件放在wwwroot. 这通常是因为这些静态文件是您不希望与网站设计文件混合的内容文件。

我们需要一个允许我们RequestPath为每个文件夹指定的提供程序。由于 Core 目前没有这样的提供者,我们只能选择自己编写。虽然不难,但这不是许多程序员喜欢处理的任务。这是一个快速实现,它并不完美,但它可以完成工作。它基于Marius Zkochanowski 提供的示例,并进行了一些改进:

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Primitives;

namespace Microsoft.Extensions.FileProviders {
  class CompositeFileWithOptionsProvider : IFileProvider {
    private readonly IFileProvider _webRootFileProvider;
    private readonly IEnumerable<StaticFileOptions> _staticFileOptions;

    public CompositeFileWithOptionsProvider(IFileProvider webRootFileProvider, params StaticFileOptions[] staticFileOptions)
  : this(webRootFileProvider, (IEnumerable<StaticFileOptions>)staticFileOptions) { }

    public CompositeFileWithOptionsProvider(IFileProvider webRootFileProvider, IEnumerable<StaticFileOptions> staticFileOptions) {
      _webRootFileProvider = webRootFileProvider ?? throw new ArgumentNullException(nameof(webRootFileProvider));
      _staticFileOptions = staticFileOptions;
    }

    public IDirectoryContents GetDirectoryContents(string subpath) {
      var provider = GetFileProvider(subpath, out string outpath);
      return provider.GetDirectoryContents(outpath);
    }

    public IFileInfo GetFileInfo(string subpath) {
      var provider = GetFileProvider(subpath, out string outpath);
      return provider.GetFileInfo(outpath);
    }

    public IChangeToken Watch(string filter) {
      var provider = GetFileProvider(filter, out string outpath);
      return provider.Watch(outpath);
    }

    private IFileProvider GetFileProvider(string path, out string outpath) {
      outpath = path;
      var fileProviders = _staticFileOptions;
      if (fileProviders != null) {
        foreach (var item in fileProviders) {
          if (path.StartsWith(item.RequestPath, StringComparison.Ordinal)) {
            outpath = path.Substring(item.RequestPath.Value.Length, path.Length - item.RequestPath.Value.Length);
            return item.FileProvider;
          }
        }
      }
      return _webRootFileProvider;
    }
  }
}

现在我们可以更新 Artak 的示例以使用新的提供程序:

app.UseStaticFiles(); //For the wwwroot folder.
//This serves static files from the given folder similar to IIS virtual directory.
var options = new StaticFileOptions {
  FileProvider = new PhysicalFileProvider(Configuration.GetValue<string>("ContentPath")),
  RequestPath = "/Content"
};
//This is required for asp-append-version (it needs to know where to find the file to hash it).
env.WebRootFileProvider = new CompositeFileWithOptionsProvider(env.WebRootFileProvider, options);
app.UseStaticFiles(options); //For any folders other than wwwroot.

在这里,我从配置文件中获取路径,因为它通常甚至完全位于应用程序的文件夹之外。/Content现在您可以使用而不是引用您的内容文件~/。例子:

<img src="~/Content/images/pic1.jpg" asp-append-version="true" />
于 2020-05-21T00:09:13.600 回答
2

如下/bower/引用时考虑去掉前缀:jquery/dist/jquery.min.js

<script type="text/javascript" src="~/jquery/dist/jquery.min.js"></script>

此外,您应该HostingEnvironment.WebRootFileProviderStartup.Configure方法中设置如下:

var compositeProvider = new CompositeFileProvider(
    env.WebRootFileProvider,
    new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "bower_components")));
env.WebRootFileProvider = compositeProvider;
var options = new StaticFileOptions()
{
    FileProvider = compositeProvider,
    RequestPath = "/bower"
};
app.UseStaticFiles(options);

希望这可以帮助。

于 2018-03-09T23:13:22.193 回答
0

我究竟做错了什么?

没有什么。根据 ASP.NET Core 的源代码,他们创建了一个FileVersionProviderWebRootPath或开始的wwwroot任务:

private void EnsureFileVersionProvider()
{
    if (_fileVersionProvider == null)
    {
        _fileVersionProvider = new FileVersionProvider(
            HostingEnvironment.WebRootFileProvider,
            Cache,
            ViewContext.HttpContext.Request.PathBase);
    }
}
于 2017-08-03T17:06:28.703 回答