正如 cmets 中的 @Akshay Gaonkar 所指出的,Microsoft 已在 ASP.NET Core (reference) 中明确评估并拒绝了此功能:
我们没有实施此计划的计划。这些概念并没有真正映射到 ASP.NET Core。 URL 本身并不基于任何目录结构。每个组件都有可能映射到目录的约定,但这不是可以概括的。
虽然a workaround is proposed using IFileProvider,它实际上不适用于虚拟目录。但是,您可以做的是建立一个映射服务来转换基本路径,并可选择查询 IIS 以动态检索这些映射,我将在下面讨论。
背景
这一限制源于 ASP.NET Core 不再与 IIS 绑定,而是依赖于抽象层(例如,IWebHostEnvironment)与 Web 服务器通信;由于默认的 ASP.NET Core Kestrel Web 服务器充当反向代理 (reference),这一事实更加复杂:
这会很艰难。我认为我们甚至不可能在当前的反向代理架构中实现。您将不得不维护一个手动映射表。
请记住,虚拟目录(或者更重要的是,虚拟应用程序)的概念对于作为 Web 服务器的 IIS 来说是相当特定的。
解决方法
不幸的是,正如前面摘录中提到的,您唯一真正的选择是在您的虚拟目录与其物理位置之间创建一个映射,然后创建一个为您处理翻译的服务。
以下是关于如何实现这一目标的基本概念验证 - 当然,您可能需要更健壮的产品代码。
界面
这引入了一种抽象,可用于依赖注入和测试目的。我坚持使用 MapPath() 以与旧版 Web 表单签名保持一致。
public interface IVirtualFileProvider
{
string MapPath(string path);
}
服务
接口的具体实现可能会从a configuration file、数据库——甚至Microsoft Web Administration library 中提取数据。然而,对于这个概念验证,我只是将它们硬编码到提供程序中:
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's answer 所示,尽管我在这里使用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));
限制
上面的代码没有努力验证文件实际上是否存在——尽管很容易通过File.Exists()添加。这显然会使通话费用更高。
动态映射
上述实现依赖于硬编码值。不过,如前所述,Microsoft Web Administration library 提供了以编程方式与 IIS 交互的方法。这包括用于从 IIS 中提取虚拟目录列表的 Application.VirtualDirectories property:
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 集成,以便在需要时动态评估可用的虚拟目录。
警告: Microsoft Web 管理库尚未更新以支持 .NET 5,并维护对不向前兼容的 .NET Core 3.x 库的依赖。目前尚不清楚微软何时或是否会发布 .NET 5 兼容版本。由于您的问题特定于 .NET Core 3.1,因此这可能不是一个直接的问题。但由于 .NET 5 是 .NET 的当前版本,因此引入对 Microsoft Web 管理库的依赖可能会带来长期风险。
结论
我知道这不是您希望的方法。但是,根据您的具体实施,这可能是一个可接受的解决方法。显然,如果这是一个可重用的库,它被放置在您不了解虚拟目录的各种站点上,您需要将数据与实现分开。不过,这至少提供了一个可以使用的基本结构。