Атрибут asp-append-version ядра ASP.NET не работает для статических файлов за пределами каталога wwwroot
У меня есть проект 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>
Что я делаю неправильно?
2 ответа
Это старый вопрос, но проблема все еще существует, и хотя решение, предложенное Артаком, работает, в большинстве случаев оно концептуально неверно. Сначала давайте посмотрим на корень проблемы:
asp-append-version
ищет файлы, используя IHostingEnvironment.WebRootFileProvider
который по умолчанию PhysicalFileProvider
указывая на wwwroot
папка.
В основных документах есть пример того, как обслуживать файлы вне корневого веб-сайта:
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
.
Артак предложил использовать CompositeFileProvider
что позволяет нам назначать более одного поставщика файлов для IHostingEnvironment.WebRootFileProvider
. Это действительно работает, но имеет фундаментальную проблему.CompositeFileProvider
не позволяет нам определить RequestPath
как в StaticFileOptions
. В качестве обходного пути Артак предложил не использовать префикс, который использует вышеупомянутое неправильное поведение, когда на файлы можно ссылаться обоими способами. Чтобы продемонстрировать проблему, предположим, что другая папка имеет такую структуру:
|_ MyStaticFiles |_ HTML | | _ privacy.html | | _ faq.html | _ изображения |_ image1.jpg
Что происходит со всеми файлами в MyStaticFiles\images
папка? При условии, чтоwwwroot
также имеет images
папка, будет ли она работать или выдаст ошибку для двух папок с одинаковыми именами? Где будет файл~/images/image1.jpg
быть откуда?
Независимо от того, работает это или нет, часто есть важная причина того, почему у вас есть статические файлы в папке, отличной от wwwroot
. Часто это происходит потому, что эти статические файлы представляют собой, например, файлы содержимого, которые вы не хотите смешивать с файлами дизайна веб-сайтов.
Нам нужен провайдер, который позволит нам указать RequestPath
для каждой папки. Поскольку Core в настоящее время не имеет такого провайдера, нам остается только написать собственный. Хотя это и не сложно, это не та задача, которую любят решать многие программисты. Вот быстрая реализация, она не идеальна, но работает. Он основан на примере, представленном Мариусом Зкохановски, с некоторыми улучшениями:
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;
}
}
}
Теперь мы можем обновить пример Артака, чтобы использовать нового провайдера:
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" />
Рассмотрите возможность удаления /bower/
префикс при обращении к jquery/dist/jquery.min.js
следующее:
<script type="text/javascript" src="~/jquery/dist/jquery.min.js"></script>
Кроме того, вы должны установить HostingEnvironment.WebRootFileProvider
в Startup.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);
Надеюсь это поможет.
Что я делаю неправильно?
Ничего такого. Согласно источнику ASP.NET Core, они создают FileVersionProvider
который начинается с WebRootPath
или же wwwroot
для этой задачи:
private void EnsureFileVersionProvider()
{
if (_fileVersionProvider == null)
{
_fileVersionProvider = new FileVersionProvider(
HostingEnvironment.WebRootFileProvider,
Cache,
ViewContext.HttpContext.Request.PathBase);
}
}