I'm trying to get the name of my ASP.NET 6 app hosted in IIS. What I need is exactly this name with proper casing:
In .NET Framework 4.8, this name was provided by HttpRequest.ApplicationPath and it was returned with proper casing (as configured in IIS, not as in the coming request's URL). However, it doesn't exist in .NET 6.
I tried:
HttpContext.Request.PathBase
, but it returns the path exactly as in the requesting URL, not as in the IIS- injecting
IServerAddressesFeature
andIWebHostEnvironment
, but none of them contains the name from IIS with correct casing IServerAddressesFeature
, but also didn't find anything relevant here- getting server variables:
IServerVariablesFeature serverVars = HttpContext.Features.Get<IServerVariablesFeature>()
and then the IIS Site name:string iis_version = serverVars["INSTANCE_NAME"]
(see documentation here), but it returns the app name in capital letters (MYSITE.WEB
)
Does anyone know how to get this site's name as configured in IIS (with proper casing)?
CodePudding user response:
TL;DR:
- Use the
APPL_MD_PATH
server-variable.- This contains the IIS Metabase Application Path string. (AFAIK, "MD" stands for "Meta-database").
- Quoteth the docs:
https://docs.microsoft.com/en-us/previous-versions/iis/6.0-sdk/ms524602(v=vs.90)?redirectedfrom=MSDN
"
APPL_MD_PATH
- Retrieves the metabase path of the application."
Like so:
// This code assumes HttpContext is available, such as in a Middleware method or `Controller` subclass.
using Microsoft.AspNetCore.Http;
String? iisMetabasePath = httpContext.GetServerVariable("APPL_MD_PATH");
// or (long-form):
String? iisMetabasePath = HttpContextServerVariableExtensions.GetServerVariable( httpContext, "APPL_MD_PATH" );
Then just trim-off the /LM/W3SVC/
part.
Note that when you run your code outside of IIS, such as with ASP.NET Core's development server, all IIS-specific data, like "APPL_MD_PATH"
won't be available, so make sure you're handling that case too.
Original research: What happened to ApplicationRoot
?
Time to bust-out ILSpy...
HttpRequest.ApplicationPath
isHttpRuntime.AppDomainAppVirtualPath
.HttpRuntime.AppDomainAppVirtualPath
isVirtualPath.GetVirtualPathStringNoTrailingSlash(HttpRuntime._theRuntime._appDomainAppVPath)
.HttpRuntime._theRuntime._appDomainAppVPath
is set inHttpRuntime.Init()
.HttpRuntime.Init()
sets_appDomainAppVPath
fromHttpRuntime.GetAppDomainString(".appVPath"))
.- "AppDomain Strings" are small serializable scalar values that are associated with each
AppDomain
. - Generally speaking, ASP.NET in .NET Framework (aka
System.Web
) creates a newAppDomain
for each Application Scope in IIS. - And of course, AppDomains no-longer exist in .NET Core and later.
- So let's find out where the
String
value for".appVPath"
comes from...
- "AppDomain Strings" are small serializable scalar values that are associated with each
System.Web.Hosting.ApplicationManager::PopulateDomainBindings
setsdict.Add(".appVPath", appVPath.VirtualPathString)
- "domain bindings" in this context refers to AppDomain bindings: nothing at all to do with DNS domain-names or
Host
header bindings in IIS. Yay for overloaded terminology.
- "domain bindings" in this context refers to AppDomain bindings: nothing at all to do with DNS domain-names or
PopulateDomainBindings
is called bySystem.Web.Hosting.ApplicationManager::CreateAppDomainWithHostingEnvironment
.- And it gets
virtualPath: VirtualPath.Create(appHost.GetVirtualPath())
.
- And it gets
appHost.GetVirtualPath()
isIApplicationHost.GetVirtualPath()
.- There are 2 in-box implementations:
System.Web.Hosting.ISAPIApplicationHost
andSystem.Web.Hosting.SimpleApplicationHost
. We're interested inISAPIApplicationHost
.
- There are 2 in-box implementations:
ISAPIApplicationHost
gets its virtualPath from the runtime argument toString appId
andString appPath
in theIAppManagerAppDomainFactory.Create
method.- And
IAppManagerAppDomainFactory
is a COM interface used directly by IIS.- The plot thickens...
- And
- At this point I got lost trawling through the legacy IIS 6 ISAPI documentation, looking for traces of the original COM definition of
IAppManagerAppDomainFactory
but came up empty-handed.- It's probably handled by
webengine4.dll
which is a native DLL and I don't have the time to bust-out Ghidra right now... - I did notice that the ISAPI request entrypoint method
HttpExtensionProc
(and itsLPEXTENSION_CONTROL_BLOCK
parameter) do not contain the IIS Application ScopeAppId
or Virtual Path, which surprised me - but most importantly: this suggests the value must likely come from theGetServerVariable
orServerSupportFunction
callbacks.... - However that's likely a waste of time anyway IIS 6 is not IIS7 and IIS7 's interfaces are no-longer called "ISAPI" but instead called just "IIS Native-Code API" (that's the most plain and boring API name I've seen in a while...).
- It's probably handled by
- So, starting with the "IIS Native-Code API" documentation I quickly found the
IWpfApplicationInfoUtil::GetApplicationPropertiesFromAppId
method (here "WPF" means "Worker Process Framework", and is entirely unrelated to the other UI-related WPF).- I also found the same data exposed via the
IMetadataInfo.GetMetaPath()
method (which returns a string of the form"LM/WEBROOT/AppHost/{SiteId}"
). - So how does an application get a
IWpfApplicationInfoUtil
interface reference?IWorkerProcessFramework->GetWpfInterface(WPF_APPLICATION_INFO_UTIL_ID)->GetApplicationPropertiesFromAppId
- So how do you get
IWorkerProcessFramework
?- It's passed as a parameter into your
w3wp.exe
worker DLL. - So let's see what
AspNetCoreModuleV2
does...
- It's passed as a parameter into your
- So how do you get
- I also found the same data exposed via the
- Actually
AspNetCoreModuleV2
uses IIS'sIHttpApplication
, derp.IHttpApplication
exposesGetAppConfigPath()
which also returns a string of the form/LM/W3SVC/1/ROOT/{Site Name}
.- ASP.NET Core seems to use it in a few places.
- At this point I gave up as it's now 6:20am, but that was a fun dive!