If you've stumbled upon this post there's a high chance that you're a seasoned ASP or ASP.NET Web Developer who is trying to find an alternative to the good old Server.MapPath method in ASP.NET Core. if I'm right, look no further, because you came to the right place.
As you most likely already know, the Server.MapPath method is a typical approach that we could use since classic ASP and ASP.NET Web Forms and MVC versions up to and including 5. Here's a typical way to use it:
1 |
string docPath = Server.MapPath("~/App_Data/docs"); |
That was a quick and effective way to access the files that we've put in the <ApplicationRoot>/App_Data/docs/ folder... and the best part of it was that it worked right out of the box, no matter if we were in Debug or Production environments.
Unfortunately this method has been removed in ASP.NET Core: how can we replace it? Well, luckily enough we have a number of possible alternatives. In this post we'll take a look at the two of them which I like the most.
IWebHostEnvironment
One of the services that's included by default when you create an ASP.NET Core application is the IWebHostEnvironment interface (which replaced the previous IHostingEnvironment that was used up-to-date until ASP.NET Core 3.0). This interface can be instantiated whithin any Controller using the standard .NET Core Dependency Injection pattern in the following way:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class HomeController : Controller { private readonly IHostingEnvironment _hostingEnvironment; public HomeController(IHostingEnvironment hostingEnvironment) { _hostingEnvironment = hostingEnvironment; } public ActionResult Index() { // application's base path string contentRootPath = _hostingEnvironment.ContentRootPath; // application's publishing path string webRootPath = _hostingEnvironment.WebRootPath; } } |
As we can see, the service has two properties of particular interest: ContentRootPath and WebRootPath.
- ContentRootPath resolves to the application's base path: this is the location of the web.config, project.json, and other configuration files.
- WebRootPath gets the app's physical publishing path, which in an ASP.NET Core Application typically is the wwwroot folder.
This basically means that we can get our /App_Code/Docs path in the following way:
1 |
string docPath = Path.Combine(_hostingEnvironment.ContentRoothPath, "App_Data/docs"); |
That's great, isn't it?
As a matter of fact, it definitely is: but what if we don't want to instantiate the IWebHostEnvironment interface in all of our application's Controller, or if we want to use such a convenient way to determine our app's physical paths outside from our Controllers (such as, for example, within a helper class)?
Well, here's a quick and effective method to allow that using AppDomain.CurrentDomain.
AppDomain.CurrentDomain
If you don't want to instantiate the IWebHostEnvironment interface in all of your Controllers, and/or if you want to access those properties from non-Controller classes, we can "store" them for later use thanks to a neat built-in feature of the AppDomain static class, which represents the application domain (the isolated environment where applications execute). Such class features a convenient CurrentDomain property with a SetData / GetData pair of methods that can be used to store arbitrary values within that secluded context.
This basically means that we can add the following line(s) of code at the end of the Configure() method of our app's Startup.cs file:
1 2 3 |
// setup app's root folders AppDomain.CurrentDomain.SetData("ContentRootPath", env.ContentRootPath); AppDomain.CurrentDomain.SetData("WebRootPath", env.WebRootPath); |
And then retrieve them from within any class (including, yet not limiting to, Controllers and Views) in the following way:
1 2 |
var contentRootPath = (string)AppDomain.CurrentDomain.GetData("ContentRootPath"); var webRootPath = string)AppDomain.CurrentDomain.GetData("WebRootPath"); |
This simple but effective technique can be further exploited to create a static helper method that will allow us to have the same functionality as the good old Server.MapPath:
1 2 3 4 5 6 7 8 9 |
public static class MyServer { public static string MapPath(string path) { return Path.Combine( (string)AppDomain.CurrentDomain.GetData("ContentRootPath"), path); } } |
And then use it in the following way:
1 |
var docPath = MyServer.MapPath("App_Data/docs"); |
Here we go!
Conclusions
That's it, at least for now: we hope that this post will help those ASP.NET Core developers that are looking for a way to replace the "classic" Server.MapPath method they were used to work with.
Thanks! I recently returned to .Net (after years) and now with Core, many things have to be upgraded.
I updated your MyServer class as the path returned (under Windows) had a mix of slashes and backslashes. I ended up going a simple Replace and now the full path is proper.
return Path.Combine((string)AppDomain.CurrentDomain.GetData(“ContentRootPath”), path.Replace(‘/’, ‘\’));
This documentation refers to a field called ‘env’. Where does this field come from? My asp .net core 6.0 program.cs does not recognize it.
If you’re using ASP.NET Core 6 with the new hosting model (without Startup.cs file), your env variable equivalent (originally a parameter of the Configure method in the Startup.cs class) is the builder.Environment property (in the Program.cs file).
Groovy.
Now, how does one get the content pointed to by the link, in my case a .PDF, into a view? Not the PDF – just the link. id \myserver\pdffoco\my.pdf.
thanx,
Doug
It’s literally explained in the post.
var contentRootPath = (string)AppDomain.CurrentDomain.GetData(“ContentRootPath”);
var webRootPath = string)AppDomain.CurrentDomain.GetData(“WebRootPath”);
With these variables, you can just do something like $”{contentRootPath}/my.pdf” or the equivalent.
Thank you.
What I meant to ask is how do we access the web hosting properties from inside a view.
First we need to inject them into the view…
@inject IWebHostEnvironment environment
Then we can access the “directly”
@environment.WebRootPath.ToString()) @Html.DisplayFor(model => model.Link))
Sorry, Ryan. I’ll try to be a little more vague with my next question. 😜