正如所指出的@阿克谢·冈卡 https://stackoverflow.com/users/4018180/akshay-gaonkar在评论中,微软已明确评估并拒绝了 ASP.NET Core 中的此功能(参考 https://github.com/dotnet/aspnetcore/issues/3824):
我们没有实施此计划的计划。这些概念在 ASP.NET Core 中并没有真正对应。 URL 本质上并不基于任何目录结构。每个组件都有可能映射到目录的约定,但这不是可以概括的。
而同时建议使用一种解决方法IFileProvider https://github.com/dotnet/aspnetcore/issues/3824#issuecomment-400107781,它实际上不适用于虚拟目录。你什么can然而,要做的是建立一个映射服务来转换基本路径,并可选择查询 IIS 以动态检索这些映射,正如我将在下面讨论的那样。
背景
这种限制源于这样一个事实:ASP.NET Core 不再依赖于 IIS,而是依赖于抽象层(例如,IWebHostEnvironment
) 与网络服务器对话;由于默认的 ASP.NET Core Kestrel Web 服务器充当反向代理(参考 https://github.com/dotnet/aspnetcore/issues/3824#issuecomment-387476614):
那会很艰难。我认为我们甚至不可能在当前的反向代理架构中实现这一点。您将必须维护一个手动映射表。
请记住,虚拟目录(或者更重要的是虚拟应用程序)的概念是专门针对 IIS 作为 Web 服务器的。
解决方法
不幸的是,正如前面的摘录中提到的,您唯一真正的选择是在虚拟目录与其物理位置之间创建映射,然后创建一个为您处理翻译的服务。
以下是如何实现这一目标的基本概念验证 - 当然,您可能需要更健壮的生产代码。
界面
这引入了可用于依赖项注入和测试目的的抽象。我一直坚持MapPath()
为了与旧版 Web 表单签名保持一致。
public interface IVirtualFileProvider
{
string MapPath(string path);
}
Service
接口的具体实现可能会从一个配置文件 https://stackoverflow.com/a/64248257/3025856、数据库——甚至是微软网络管理库 https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.administration.virtualdirectorycollection?view=iis-dotnet。然而,对于这个概念验证,我只是将它们硬编码到提供程序中:
public class VirtualFileProvider: IVirtualFileProvider
{
// Store dependencies
private readonly string _webRootPath;
// Map virtual directories
private readonly Dictionary<string, string> _virtualDirectories = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
{ "Images", @"\\192.168.1.1\images" }
};
public VirtualFileProvider(string webRootPath) {
_webRootPath = webRootPath;
}
public string MapPath(string path)
{
// Validate path
if (String.IsNullOrEmpty(path) || !path.StartsWith("/", StringComparison.Ordinal)) {
throw new ArgumentException($"The '{path}' should be root relative, and start with a '/'.");
}
// Translate path to UNC format
path = path.Replace("/", @"\", StringComparison.Ordinal);
// Isolate first folder (or file)
var firstFolder = path.IndexOf(@"\", 1);
if (firstFolder < 0)
{
firstFolder = path.Length;
}
// Parse root directory from remainder of path
var rootDirectory = path.Substring(1, firstFolder-1);
var relativePath = path.Substring(firstFolder);
// Return virtual directory
if (_virtualDirectories.ContainsKey(rootDirectory))
{
return _virtualDirectories[rootDirectory] + relativePath;
}
// Return non-virtual directory
return _webRootPath + @"\" + rootDirectory + relativePath;
}
}
登记
该实现需要了解默认 Web 根目录,以便转换不在虚拟目录中的文件的路径。这可以动态检索,如所示@Pashyant Srivastava 的回答 https://stackoverflow.com/a/67570843/3025856,虽然我正在使用IWebHostEnvironment
这里。这样,您就可以注册VirtualFileProvider
作为具有 ASP.NET Core 依赖注入容器的单例生活方式:
public class Startup
{
private readonly IWebHostEnvironment _hostingEnvironment;
public Startup(IWebHostEnvironment webHostEnvironment)
{
_hostingEnvironment = webHostEnvironment;
}
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
// Register virtual file provider
services.AddSingleton<IVirtualFileProvider>(new VirtualFileProvider(_hostingEnvironment.WebRootPath));
}
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
…
}
}
执行
注册实现后,您可以将提供程序注入到 MVC 控制器的构造函数中,甚至直接注入到您的操作中:
public IActionResult MyAction([FromServices] IVirtualFileProvider fileProvider, string file)
=> Content(fileProvider?.MapPath(file));
局限性
上面的代码没有努力验证该文件actually存在——尽管很容易通过以下方式添加File.Exists() https://learn.microsoft.com/en-us/dotnet/api/system.io.file.exists?view=net-5.0。这显然会使通话费用更高一些。
动态映射
上述实现依赖于硬编码值。不过,正如前面提到的,微软网络管理库 https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.administration提供以编程方式与 IIS 交互的方法。这包括Application.VirtualDirectories财产 https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.administration.application.virtualdirectories?view=iis-dotnet用于从 IIS 中提取虚拟目录列表:
var directories = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var manager = new ServerManager();
var site = manager.Sites["Default Web Site"];
var application = site[0];
foreach (var virtualDirectory in application.VirtualDirectories)
{
directories.Add(virtualDirectory.Path, virtualDirectory.PhysicalPath);
}
这可以与VirtualFileProvider
如果需要,动态评估可用的虚拟目录。
Warning:Microsoft Web 管理库尚未更新为支持 .NET 5,并且维护对不向前兼容的 .NET Core 3.x 库的依赖关系。目前尚不清楚 Microsoft 何时或是否会发布 .NET 5 兼容版本。由于您的问题特定于 .NET Core 3.1,因此这可能不是一个紧迫的问题。但由于 .NET 5 是 .NET 的当前版本,引入对 Microsoft Web 管理库的依赖可能会带来长期风险。
结论
我知道这不是您所希望的方法。然而,根据您的具体实现,这可能是一个可接受的解决方法。显然,如果这是一个可重用的库,放置在您不了解虚拟目录的各种站点上,则需要将数据与实现分开。不过,这至少提供了一个可以使用的基本结构。