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

时间:2017-03-13 23:21:07

标签: asp.net-mvc wwwroot asp-append-version

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

我可以通过将这些文件添加到我的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>

我做错了什么?

3 个答案:

答案 0 :(得分:2)

在引用foreach($dbh->query('SELECT PlayerName, GROUP_CONCAT(BallotNumber) 时,请考虑删除foreach($dbh->query('SELECT PlayerName, GROUP_CONCAT(BallotNumber SEPARATOR ', ') 前缀,如下所示:

/bower/

另外,您应该在jquery/dist/jquery.min.js方法中设置<script type="text/javascript" src="~/jquery/dist/jquery.min.js"></script> ,如下所示:

HostingEnvironment.WebRootFileProvider

希望这有帮助。

答案 1 :(得分:0)

  

我做错了什么?

无。 According to ASP.NET Core's source,他们创建了一个FileVersionProvider,从WebRootPathwwwroot开始执行此任务:

private void EnsureFileVersionProvider()
{
    if (_fileVersionProvider == null)
    {
        _fileVersionProvider = new FileVersionProvider(
            HostingEnvironment.WebRootFileProvider,
            Cache,
            ViewContext.HttpContext.Request.PathBase);
    }
}

答案 2 :(得分:0)

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

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

Core文档中有一个有关如何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"
    });
}

这使您可以同时管理wwwrootMyStaticFiles文件夹中的静态文件。如果您有图片\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

现在,MyStaticFiles\images文件夹中的所有文件会怎样?假设wwwroot也有images文件夹,那么对于两个同名文件夹,它是否可以工作或给您带来错误?文件~/images/image1.jpg来自何处?

无论它是否有效,通常都有一个重要原因,说明为什么您的静态文件位于wwwroot以外的文件夹中。通常是因为这些静态文件是您不希望与网站设计文件混合的内容文件。

我们需要一个提供程序,允许我们为每个文件夹指定RequestPath。由于Core当前没有此类提供程序,因此我们只能选择编写自己的提供程序。尽管并不困难,但这并不是许多程序员喜欢解决的任务。这是一个快速的实现,虽然不是完美的,但是可以完成工作。它基于example provided by 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" />