aspnet / IISIntegration

[Archived] ASP.NET Core IIS integration. Project has moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
148 stars 59 forks source link

IIS virtual directories #14

Closed Tratcher closed 8 years ago

Tratcher commented 8 years ago

When we're spun up behind IIS / HttpPlatformHandler how do we know what the virtual directory is? We need this to correctly set PathBase in the app. Ideally we'd set PathBase as part of the initial server address configuration, but at that point we're only given the port via environment variable. @davidfowl

davidfowl commented 8 years ago

Should handle that based on the path passed into server.urls

Tratcher commented 8 years ago

Yes, but how do you get that information from IIS to server.urls? Right now we only get the port.

davidfowl commented 8 years ago

We'd need to restructure a bit. It can be setup in the M.A.Hosting.json file.

Tratcher commented 8 years ago

Depends on https://github.com/aspnet/KestrelHttpServer/issues/214

Tratcher commented 8 years ago

Workaround: put your app in a app.Map("/vdir" ... https://github.com/aspnet/Hosting/issues/416#issuecomment-149046552

rubenprins commented 8 years ago

Please don't require us to hard code the IIS vdir in external config files, like M.A.Hosting.json, when IIS and HttpPlatformHandler already know this.

Just pass this information on for example in an environment variable, like %HTTP_PLATFORM_VDIR%, so you can say

    <httpPlatform stdoutLogEnabled="false" stdoutLogFile="log.log" startupTimeLimit="20" processPath="%HOME%\site\kestrel.cmd" arguments="http://localhost:%HTTP_PLATFORM_PORT%/%HTTP_PLATFORM_VDIR%">
      <environmentVariables></environmentVariables>
    </httpPlatform>
davidfowl commented 8 years ago

@rubenprins That fix is coming. This is a workaround until the fix is available. We're working with the httpPlatformHandler team to fix bugs found in beta8 and rc1.

Fosol commented 8 years ago

I don't understand the workaround.

Are you saying we need to configure the sub-application within the parent application. Essentially copying the Configure method of the sub-application into the parent?

Tratcher commented 8 years ago

Sup-apps have different paths (e.g. /subapp/controller/foo) which breaks routing. The workaround is to use Map("/subapp"... In your sub app to remove the extra path segment.

pksorensen commented 8 years ago
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            app.Map("/testvirt", map => Configure1(map, env, loggerFactory));
        }
        // Configure is called after ConfigureServices is called.
        public void Configure1(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {

            loggerFactory.MinimumLevel = LogLevel.Information;
            loggerFactory.AddConsole();
            loggerFactory.AddDebug();

            // Add the platform handler to the request pipeline.
            app.UseIISPlatformHandler();

            app.UseDefaultFiles();
            // Configure the HTTP request pipeline.
            app.UseStaticFiles();

        }

I havent been able to get the workaround to work with an app configuration above.

Tratcher commented 8 years ago

Updated workaround with the latest RC2 packages (https://github.com/aspnet/IISIntegration/issues/52#issuecomment-171444840).

The next version of HttpPlatformHandler (1.3?) will provide the base path via HTTP_PLATFORM_APPL_PATH and UseIISPlatformHandlerUrl will apply it. Until that version is released you can add HTTP_PLATFORM_APPL_PATH to the environment variables section of the httpPlatform web.config with a value like /MySubApplication.

RE: https://github.com/aspnet/IISIntegration/blob/dev/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerAddressExtensions.cs#L31

tuespetre commented 8 years ago

@Tratcher :beers:

All y'all winning at life and such

pksorensen commented 8 years ago

I also got my stuff working with a little help from @davidfowl

DavidR91 commented 8 years ago

Until that version is released you can add HTTP_PLATFORM_APPL_PATH to the environment variables section of the httpPlatform web.config with a value like /MySubApplication.

Just to clarify here - does this workaround function on its own, like this:

<httpPlatform processPath="..\approot\web.cmd" arguments="" stdoutLogEnabled="false" stdoutLogFile="..\logs\stdout.log" startupTimeLimit="3600">
      <environmentVariables>
        <environmentVariable name="HTTP_PLATFORM_APPL_PATH" value="testbed" />
      </environmentVariables>
</httpPlatform>

or does it need to be done in tandem with the Configure changes mentioned previously?

Also, is this expected to work in HTTP Platform Handler 1.2, or do we need some kind of nightly/RC?

[Because as it stands, I've had no luck with just the web.config change and HTTP handler 1.2]

Tratcher commented 8 years ago

That should be value="/testbed", and yes it should work with HttpPlatformHandler 1.2 and our latest RC2 packages. This should replace the Map workaround.

tuespetre commented 8 years ago

Thanks for together providing a visible, valid example, @DavidR91 and @Tratcher

galmok commented 8 years ago

I tried adding the environmentVariables element to web.config source file, but dnu publish removes it when inserting DNX_PATH and DNX_ARGS. I tried with dnu rc2-16357. I guess this requires a post-publish modification.

wmerifield commented 8 years ago

this is what we refer to as a FON! a F#^%$#K UP OF NOTE! Have tried every single one of the fixes mentioned here and elsewhere ...6 hours & still no end in sight! Have no idea what the heck you blokes are doing that side but its not working for the rest of us plebs!

skyflyer commented 8 years ago

There seems to be a lot of confusion about this feature, so I suggest we get some troubleshooting tips on how to see what's going on behind the scenes. My situation:

  1. Deploy fresh MVC 6 app (with ASP.NET and Web Tools 2015 (RC1 Update 1)) to a new site: Works
  2. Deploy another fresh MVC 6 app to a virtual directory (does not work (HTTP 500 error, no details)
  3. Deploy fresh MVC 6 app under a new virtual directory under the default web site (HTTP 404)

While inspecting the application log files (I've configured the httpPlatform element with stdoutLogEnabled set to true), I see that the requests go to /virtual directory where the app should be deployed, but the app itself can't handle this. I also see that the web.config looses the environment settings (environmentVariables element) when "publishing the application to file system" (that's probably unrelated issue).

So, neither the solution with the web.config nor the app.Map() works (in my case) for a simple scenario of deploying the new ASP.NET MVC 6 app under the virtual directory. Time permitting, I'll test with an almost empty ASP.NET 5 app.

What is also very puzzling is that IIS is not serving the static files, but kestrel is.

Questions:

  1. how exactly is deployment under a virtual root supposed to work?
    1. will app nesting be supported as well? (I guess it is related to the second question)
  2. will IIS be serving the static files or will those files be served by kestrel?
    1. if static assets will be served by kestrel, is then IIS just a reverse proxy? (http platform handler, really)
dotcom9 commented 8 years ago

This is causing me one hell of a headache! Is there any workaround with Asp.Net Core RC1 and HttpPlatformHandler 1.2, or nothing until the next releases?

tuespetre commented 8 years ago

@dotcom9:

With RC1 all you should need to do is wrap the contents of Startup.Configure with app.Map("/virtual-path-name", ...).

@skyflyer:

skyflyer commented 8 years ago

@tuespetre,

  1. so you're saying IIS will be relegated to proxying kestrel?
  2. regarding app nesting: you're right. I've been hitting a bug before that I couldn't get a trace on (I've since installed the error reporting pages to IIS).
tuespetre commented 8 years ago

@skyflyer:

Pretty much, which is a good thing IMO.

pksorensen commented 8 years ago

I have been successfully deploying RC1 apps to separate virtual directories. Just haven't had the time to share the setup and was thinking that it all would change with RC2. But since it has not come out yet, i will try share here.

Install DNVM (if needed on build server)

image

the script looks like this:

# bootstrap DNVM into this session.
&{$Branch='dev';iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.ps1'))}

# load up the global.json so we can find the DNX version
$globalJson = Get-Content -Path $PSScriptRoot\..\global.json -Raw -ErrorAction Ignore | ConvertFrom-Json -ErrorAction Ignore

if($globalJson)
{
    $dnxVersion = $globalJson.sdk.version
}
else
{
    Write-Warning "Unable to locate global.json to determine using 'latest'"
    $dnxVersion = "latest"
}

# install DNX
# only installs the default (x86, clr) runtime of the framework.
# If you need additional architectures or runtimes you should add additional calls
# ex: & $env:USERPROFILE\.dnx\bin\dnvm install $dnxVersion -r coreclr
& $env:USERPROFILE\.dnx\bin\dnvm install $dnxVersion -Persistent

 # run DNU restore on all project.json files in the src folder including 2>1 to redirect stderr to stdout for badly behaved tools
Get-ChildItem -Path $PSScriptRoot\.. -Filter project.json -Recurse | ForEach-Object { & dnu restore $_.FullName 2>1 }

(Notice that i placed it in a build dir in my respository and it should look for project.json in the parent folder).

Build Arguments

image

where the arguments for msbuild are

/p:TypeScriptCompileBlocked=true /t:Build,FileSystemPublish /p:PublishConfiguration=$(BuildConfiguration) /p:PublishOutputPathNoTrailingSlash=$(Build.SourcesDirectory)\artifacts\$(BuildConfiguration)\Publish

the TypescriptCompile is optional for this and something we build with grunt instead of vs. In VS2015 this is similar to publish to file system.

Move web.config to parent.

image

$(Build.SourcesDirectory)\artifacts\$(BuildConfiguration)\Publish\wwwroot\web.config $(Build.SourcesDirectory)\artifacts\$(BuildConfiguration)\Publish\web.config

You can do this after publish to file system manual if you dont use a build system. The reason for moving it to the parent is that we want each virtual app to site side by side.

/app1
/app2
/app3

and in each app you want

/app1/wwwroot
/app1/approot
/app1/web.config

One could also take a look at this SO Q/A : http://stackoverflow.com/questions/34784391/deploying-aspnet5-apps-to-virtual-directories-on-azure-websites where i wrote about it when i first ran into the problem.

Publishing

Again I am using a build system. image

What is needed to be done is that the httpPlatformPath needs to be updated. I am doing this here: https://gist.github.com/s093294/e97de232ee6d9c563c8b#file-azurewebappdeploymenttask-cs-L189 and from the input parameters you may see that this is set to .\approot\web.cmd (remember the folder structure above).

Environment and Startup Fixes (The real fix of this github issue).

Setting the Environment variable (which my build task also do) ends up with a web.config that looks like this:

<configuration>
  <system.webServer>
    <handlers>
      <remove name="httpplatformhandler" />
      <add name="httpplatformhandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" />
    </handlers>
    <httpPlatform processPath=".\approot\web.cmd" arguments="" stdoutLogEnabled="true" stdoutLogFile="%home%\LogFiles\stdout.log" startupTimeLimit="3600">
      <environmentVariables>
        <environmentVariable name="VIRTUAL_PATH" value="/pr/ascend-ammo-wildlife/47" />
      </environmentVariables>
    </httpPlatform>
  </system.webServer>
</configuration>

where the VIRTUAL_PATH has to match the IISDir, where mine can be verified to match the azure website settings: image

With the following startup class it works locally and deployed:

public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
        }

        public void ConfigureServices(IServiceCollection services)
        {
        }

        public void Configure2(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {

            loggerFactory.MinimumLevel = LogLevel.Information;
            loggerFactory.AddConsole();
            loggerFactory.AddDebug();

            app.UseIISPlatformHandler();       

            app.UseFileServer(true);

        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {

            if(!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("VIRTUAL_PATH")))
                app.Map(Environment.GetEnvironmentVariable("VIRTUAL_PATH"), (myAppPath) => this.Configure2(myAppPath, env,loggerFactory));
            else
                Configure2(app, env, loggerFactory);
        }
        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }

Question ?

Since I have all this configured in Visual Studio Team Services and could package it as an extension. If you feel that this is valuable if you could click and install the steps and make VSTS deploy it for you in the same setup as above. Feel free to send me a email at pks@s-innovations.net. I have been thinking about how to distribute it and would like to ask a few questions. (Also remember that VSTS is free up to 5 users, so even if you dont use it - it is easy to start using it).

dotcom9 commented 8 years ago

@tuespetre thanks

Tratcher commented 8 years ago

This as been fixed in AspNetCoreModule v0.8: https://github.com/aspnet/IISIntegration/issues/105.

leo9223 commented 7 years ago

Asp.net core still not working under virtual directory in IIS

Tratcher commented 7 years ago

@leo9223 virtual directories or sub applications? These are commonly confused. Sub applications should work but there's no support for virtual directories.

leo9223 commented 7 years ago

@Tratcher Virtual directories are not working, but when I convert that directory to application then it is working. yea sub application is working.

leo9223 commented 7 years ago

@Tratcher However different webconfigs working for IIS10 and IIS8.5

on IIS10 app works with this setting <aspNetCore processPath="dotnet" arguments=".\myApp.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />

on IIS8.5 app works with this setting <aspNetCore processPath="C:\Program Files\dotnet\dotnet.exe" arguments=".\myApp.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />

I don't know why. can you please tell me why it is happening?

Tratcher commented 7 years ago

Your IIS 10 machine probably has dotnet on the system path, where your other machine doesn't. Did you restart the second machine after the install?

leo9223 commented 7 years ago

@Tratcher I just restarted the IIS not the machine, let me check. Thank you

guardrex commented 7 years ago

@leo9223 Just for future reference, @pan-wang advised for the docs that you should be able to avoid a restart if you execute net stop was /y followed by net start w3svc.

buddalasunil999 commented 7 years ago

I have the same problem.. I want to add ASP.NET Web API application (Admin) under ASP.NET Core website (Test) as shown in the image below. But the admin doesn't work because of the corehandler. How do I ask the corehandler to just work with the root application but not the applications inside it. image

Tratcher commented 7 years ago

@buddalasunil999 Web.Config files are inherited by child apps. inside your admin web.config in the handlers section you can remove the aspNetCore handler that was added in the parent site.

buddalasunil999 commented 7 years ago

@Tratcher Yeah just figured it out.. it's inherting the handler, added remove handler to child config and started working. Thanks

sturlath commented 7 years ago

@Tratcher you say "there's no support for virtual directories.". After I saw this comment I have searched everywhere to have it confirmed without finding anything on it, except in comments from you.

Are you saying I will have to set up a new site and deploy there?

So the setup below will never work because posservice is a virtual directory?

fyrirspurn

What I need is to be able to call this web api url ? https://A.HTTPS.Site/posservice/api/v1/authentication/accesstoken

My api controller attribute routing looks like [Route("api/v1/authentication")] with this on a method [HttpPost("accesstoken"), AllowAnonymous]

  1. Create a sub application (lets call it POS) straight under A.HTTPS.site
  2. Where do I point the service (physical path) to? Where does the "api/v1" part plug in? Nb. I tried every version I could think of, to no avail.

I have tried just about everything under the sun I believe (even some map https://github.com/aspnet/IISIntegration/issues/14#issuecomment-150716529 things).. so some help would be appreciated :-)

Tratcher commented 7 years ago

Yes, if you made the posservice level a sub-application and pointed it at your ASP.NET Core app then it should work. Also remove the nested posservice/api and /posservice/api/v1 sub-applications definitions, they'll mess up your routing. Any segments marked as a sub-application get moved from HttpContext.Path to HttpContext.PathBase and Rout only operates on Path.

evil-shrike commented 7 years ago

Hi, previously on full .net there was System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath which returned virtual dir/app name. Having HttpContext.PathBase is very nice, but it exists only during request handling, which ApplicationVirtualPath was available in IHttpModule.Init for example.

Is there any way to get base path before on starup/configure (before request processing)?

davidfowl commented 7 years ago

@evil-shrike why do you need it?

evil-shrike commented 7 years ago

@davidfowl I'm migrating some app/lib onto core from mvc5 and there I have some global singleton config (let's call it XConfig) which is available via DI for other components. I put the base path from ApplicationVirtualPath into that XConfig on start. Then for example some View calls its Model's method, which returns some html markup, for this it uses other injected component which only has XConfig and takes basePath from it. Now I have to pass HttpRequest all the way (better), on initialize XConfig on first request (worse). I'm not telling that it will be impossible or bad but just decided to ask. As probably server (IIS) can pass this info on initializing not only on request handling.

bharathpbk commented 6 years ago

Hi when i deploy asp.net core 2.0 with IIS Windows authentication I am getting Cors Error with angular. Please suggest any example