I have an ASP.NET Core 3.0 MVC application with images in it. E.g.,
http://foo.bar/images/image.jpg
Now, the folder images
is a virtual directory which is mapped to a network drive, such as \\192.168.1.1\images
.
Question:
What method turns the information /images/image.jpg
into \\192.168.1.1\images\image.jpg
? I need to retrieve the physical path of the file from the relative web path.
In ASP.NET Web Forms, this could be done by something like Server.MapPath("~/images/image.jpg")
, but this method doesn't exist in ASP.NET Core's HttpContext
anymore.
As noted by @Akshay Gaonkar in the comments, Microsoft has explicitly evaluated and rejected this functionality in ASP.NET Core (reference):
And while a workaround is proposed using
IFileProvider
, it doesn't actually work for virtual directories. What you can do, however, is establish a mapping service for translating the base path—and optionally querying IIS to retrieve those mappings dynamically, as I’ll discuss below.Background
This limitation stems from the fact that ASP.NET Core is no longer tied to IIS, but instead relies on an abstraction layer (e.g.,
IWebHostEnvironment
) to talk to the web server; that is further complicated by the fact that the default ASP.NET Core Kestrel web server acts as a reverse proxy (reference):Keep in mind that the concept of a virtual directory (or, even more so, a virtual application) is fairly specific to IIS as a web server.
Workaround
Unfortunately, as mentioned in the previous excerpt, your only real option is to create a mapping between your virtual directories and their physical locations, and then create a service that handles the translation for you.
The following is a basic proof-of-concept for how you might accomplish that—though, of course, you'll probably want something more robust for production code.
Interface
This introduces an abstraction that can be used for dependency injection and testing purposes. I've stuck with
MapPath()
for consistency with the legacy Web Forms signature.Service
The concrete implementation of the interface might pull the data from a configuration file, a database—or even the Microsoft Web Administration library. For this proof-of-concept, however, I'm just hard-coding them into the provider:
Registration
The implementation requires knowledge of the default web root, for translating the path for files not in a virtual directory. This can be retrieved dynamically, as seen in @Pashyant Srivastava's answer, though I'm using the
IWebHostEnvironment
here. With that, you can register theVirtualFileProvider
as a singleton life style with ASP.NET Core's dependency injection container:Implementation
With your implementation registered, you can inject the provider into your MVC controller's constructor or even directly into your action:
Limitations
The above code makes no effort to validate that the file actually exists—though that's easy to add via
File.Exists()
. That will obviously make the call a bit more expensive.Dynamic Mapping
The above implementation relies on hard-coded values. As mentioned, though, the Microsoft Web Administration library offers methods for interacting with IIS programmatically. This includes the
Application.VirtualDirectories
property for pulling a list of virtual directories from IIS:This can be integrated with the
VirtualFileProvider
to dynamically assess the available virtual directories if needed.Conclusion
I know this isn't the approach you were hoping for. Depending on your exact implementation, however, this might be an acceptable workaround. Obviously, if this is a reusable library being placed on a variety of sites where you have no knowledge of the virtual directories, you'll need to separate the data from the implementation. This at least provides a basic structure to work off of, though.