c# - ASP.NET Core asp-append-version 属性不适用于 wwwroot 目录之外的静态文件

标签 c# asp.net asp.net-core wwwroot asp-append-version

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

我可以通过将它添加到我的 Startup.cs 来为这些文件提供服务。类(class):

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>

我究竟做错了什么?

最佳答案

这个问题很老了,但问题仍然存在,虽然 Artak 提供的解决方案有效,但在大多数情况下它在概念上是不正确的。首先让我们看看问题的根源:
asp-append-version使用 IHostingEnvironment.WebRootFileProvider 查找文件默认情况下是 PhysicalFileProvider指向wwwroot文件夹。

核心文档有一个关于如何 serve files outside of web root 的示例:

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 ,因此其文件不应与根 / 合并.但至少它有效,它给了你你想要的。

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

Artak 建议使用 CompositeFileProvider这允许我们将多个文件提供者分配给 IHostingEnvironment.WebRootFileProvider .这确实有效,但它有一个根本问题。 CompositeFileProvider不允许我们定义 RequestPath喜欢在 StaticFileOptions .作为一种解决方法,Artak 建议我们不应该使用前缀,这利用了上述错误行为,即文件可以通过两种方式引用。为了演示这个问题,假设另一个文件夹具有如下结构:
|_ MyStaticFiles
       |_ HTML
       | |_ privacy.html
       | |_ faq.html
       |_ images
         |_ image1.jpg

Now, what happens to all files in the MyStaticFiles\images folder? Assuming that wwwroot also has images folder, will it work or give you an error for two identically named folders? Where will the file ~/images/image1.jpg be coming from?

Regardless of whether it works or not, there is often an important reason for why you have your static files in a folder other than wwwroot. It is often because those static files are for example content files that you don't want mixed with website design files.

We need a provider that allows us to specify the RequestPath for each folder. Since Core doesn't currently have such provider, we're only left with the option of writing our own. Although not difficult, it's not a task that many programmers like to tackle. Here is a quick implementation, it's not perfect, but it does the job. It is based on the example provided by Marius Zkochanowski with some inhancements:

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" />

关于c# - ASP.NET Core asp-append-version 属性不适用于 wwwroot 目录之外的静态文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42775181/

相关文章:

c# - 在辅助显示器上启动 WPF?

c# - 当前时区时间

c# - ASP.NET CORE Web 应用程序抛出 SQLiteException : unable to open database file

asp.net - Signalr - 无尽的 $.connection.hub.disconnected 事件?

c# - Asp.Net-Core - OutputFormatter 将小数结果格式化为两位小数

asp.net-core - .NET Core 3.1 - dotnet ef 添加迁移错误

c# - 如何使用不同的字符编码构建项目?

c# - 使用 UTF-8 在页面标题 ASP.NET 中编码西类牙文特殊字符

asp.net-core - 在 db ASP.NET Core 中临时保存密码

c# - 在 silverlight 中加载文件