5

I'm experiencing a strange behavior when launching an ASP.NET core 3.1 web application from Visual Studio 2019 using kestrel (I mean the launching profile which does not use IIS express).

I have created a minimal application to reproduce the issue.

OS: Windows 10 (build 19041.746) Visual Studio version: Visual Studio 2019 version 16.8.4

This is the csproj file:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>


</Project>

This is the launchSettings.json file:

{
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:52222",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "weatherforecast",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "TestWebApplication": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "weatherforecast",
      "applicationUrl": "http://localhost:5002",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

This is the Program.cs file:

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace TestWebApplication
{
  public class Program
  {
    public static void Main(string[] args)
    {
      CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
              webBuilder.UseStartup<Startup>();
            });
  }
}

This is the Startup.cs file:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace TestWebApplication
{
  public class Startup
  {
    public Startup(IConfiguration configuration)
    {
      Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddControllers();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment())
      {
        app.UseDeveloperExceptionPage();
      }

      app.UseRouting();

      app.UseAuthorization();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapControllers();
      });
    }
  }
}

When I launch this web application by using the TestWebApplication profile from Visual Studio everything works fine: kestrel is launched at http://localhost:5002 and the web browser is launched at http://localhost:5002/weatherforecast.

This is exactly what I would expect from the provided launchSettings.json file.

Consider the following change to the Program.cs, made to customize the application configuration sources:

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

namespace TestWebApplication
{
  public class Program
  {
    public static void Main(string[] args)
    {
      CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration(builder =>
            {
              builder.Sources.Clear();
              builder.AddEnvironmentVariables();
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
              webBuilder.UseStartup<Startup>();
            });
  }
}

This change breaks everything in terms of interaction with the launchSettings.json file. The following happens:

  • kester is launched on both http://localhost:5000 and https://localhost:5001
  • web browser is not launched on http://localhost:5002/weatherforecast

Basically it seems that the file launchSettings.json is ignored and some defaults are used instead.

I have also observed the following:

  • the root cause seems to be the line which clears the configuration sources: builder.Sources.Clear();
  • the IIS express profile is not affected by this issue: it works as expected even with the modified Program.cs file

Is this a bug ? Am I missing anything ?

SOME CLARIFICATIONS

I'm clearing the configuration sources in order to have full control over the configuration sources themselves. Put another way, I would like to remove all the existing configuration sources and start from scratch. According to this documentation this approach seems to be allowed.

Notice that the exact same code works as expected when using the WebHost class instead of the Host class. In that case the configuration sources can be fully customized and the ability to read the launchSettings.json file is maintained for Kestrel too. I'm trying to achieve the same effect with the generic Host class, which is the recommended class to be used instead of WebHost in ASP.NET core 3.1

Enrico Massone
  • 6,464
  • 1
  • 28
  • 56
  • When you clear your configuration sources you've removed whatever was telling Kestrel to bind to port 5002. This doesn't effect using IIS Express because your app itself isn't listening on port 5002, IIS is and is reverse-proxying the requests to whatever Kestrel is using internally. – Valuator Feb 09 '21 at 21:27
  • @Valuator I'm doing so because I want to fully customize the configuration sources. The idea is removing all the existing sources and start from scratch. According to [this docs](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#json-configuration-provider) clearing the sources is supported. – Enrico Massone Feb 09 '21 at 21:39
  • @Valuator the same code works perfectly with ASP.NET core 2.2 and the `WebHost`. In that case clearing the configuration sources does **not** break the ability to read `launchSettings.json` and the `5002` port is maintained for kestrel too. Do you know how to achieve the same effect by using ASP.NET core 3.1 and the generic `Host` class ? – Enrico Massone Feb 09 '21 at 21:42
  • @Valuator when using `WebHost` instead of `Host`, **this exact same code works in ASP.NET core 3.1 too**. The problem seems to pop up only when using the generic `Host` class. – Enrico Massone Feb 09 '21 at 21:52
  • @Valuator just a little side note. Starting from ASP.NET core 3.0 the default hosting model under IIS is the so called in-process hosting model. See [this documentation](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/in-process-hosting?view=aspnetcore-5.0#enable-in-process-hosting). So I guess that IIS express is not actually acting as a reverse proxy for kestrel. – Enrico Massone Feb 09 '21 at 23:01

1 Answers1

6

I have finally managed this issue.

That's not an ASP.NET core 3.1 bug, insted this is the way the generic Host is designed to work.

Basically the configuration for the port number which is specified inside the lanchSettings.json file is passed to the ASP.NET core application by means of some environment variables prefixed by ASPNETCORE_. These environment variables are set automatically by Visual Studio. The one responsible for the kestrel port binding is called ASPNETCORE_URLS.

When doing builder.Sources.Clear(); the configuration source for the environment variables prefixed by ASPNETCORE_ is cleared away. So, in order to fix the issue, it is necessary to replace this configuration source:

.ConfigureAppConfiguration(builder =>
            {
              builder.Sources.Clear();
              builder.AddEnvironmentVariables();
              builder.AddEnvironmentVariables("ASPNETCORE_");
            })

All the details are available here

Enrico Massone
  • 6,464
  • 1
  • 28
  • 56