Reading port from configuration file in Program class Main method

When running ASP.NET Core application as self-hosted using Kestrel, it will by default use port 5000 for listening for requests. This should not be a big issue since you can always proxy requests to port 5000.

Problem occurs when you might want to have multiple applications on one host, or you simply cannot use port 5000 for security or any other reasons.

Luckily, default port for ASP.NET Core application running on Kestrel can be easily changed, but in order to make it easily configurable there are few changes you need to make in your code in order to access IHostingEnvironment and IConfiguration implementation instances combined.

Hosting environment: Development

Content root path: C:\Temp\Sample.Web.Api

Now listening on: http://localhost:5000

Application started. Press Ctrl+C to shut down.

Updating port in Program class using extension method

Kestrel port can easily configured with UserUrls extensions in Main method in Program class.

public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseUrls("http://localhost:5200") .UseKestrel() .UseStartup<Startup>(); }

It is easy, straight forward and as you can see from the code snippet above, you do not have to change default Program class template too much. Just one extension method call with your localhost URL with port and you are good to got to use new port.

The problem with this approach is that your port is pretty much hard coded and in order to change it, you need to access your code, change it and then compile it again in order to start using it. All these problems can be overcome by setting up CI/CD, but in situations when this is not the code, for example for small projects, updating code, re-compiling and deploying might be an overhead.

Configuring port using JSON configuration file

Another way to change default port which does not require code re-compilation is simply storing it in JSON configuration file.

{ "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } }, "Host": { "Port": 5200 } }

Now, configuration dependency injection is not available straight away in Program class, so you need to instantiate ConfigrationBuilder, set the options and create configuration instance by invoking Build method on the ConfigurationBuilder class instance.

var config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false);

We'll add this configuration instantiating to CreateWebHostBuilder method and change it a little bit.

public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) { var config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false) .Build(); var webHost = WebHost.CreateDefaultBuilder(args) .UseUrls($"http://localhost:{config.GetValue<int>("Host:Port")}") .UseKestrel() .UseStartup<Startup>(); return webHost; } }

Once ConfigurationBuilder class is instantiated, we can use the IConfiguration implementation instance produced by invoking Build method. Using GetValue<T> extension method we can fetch port value as integer and then using string interpolation we add it as a part of URL passed to UseUrls extension method.

Hosting environment: Development

Content root path: C:\Temp\Sample.Web.Api

Now listening on: http://localhost:5200

Application started. Press Ctrl+C to shut down.

This way we took the port value out of our code and we can change it without any code re-compilation. However, our configuration is does not reflect environment where application is running. To combine hosting environment and configuration file we need to do some additional changes in our Program class.

Reading port from environment related configuration file

ASP.NET Core has great way to use different configuration based on the hosting environment values. By default, ASP.NET Core environment name relies on ASPNETCORE_ENVIRONMENT variable. By simply setting in on a different host, your application can load different configuration file and therefore use different dependencies based on the environment.

To load different configuration file, we need to access IHostingEnvironment instance. Luckily, in .NET Core starting from 2.0 version IHostingEnvironment is automatically injected so we can access t by searching thought predefined services.

public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) { IHostingEnvironment hostingEnvironment = null; var webHost = WebHost.CreateDefaultBuilder(args) .UseContentRoot(Directory.GetCurrentDirectory()) .ConfigureServices( services => { hostingEnvironment = services .Where(x => x.ServiceType == typeof(IHostingEnvironment)) .Select(x => (IHostingEnvironment)x.ImplementationInstance) .First(); }) .UseKestrel(options => { var config = new ConfigurationBuilder() .AddJsonFile($"appsettings.{hostingEnvironment.EnvironmentName}.json", optional: false) .Build(); options.Listen(IPAddress.Loopback, config.GetValue<int>("Host:Port")); }) .UseStartup<Startup>(); return webHost; } }

Once we have IHostingEnvironment instance fetched, we can use it for pointing to configuration file with environment name in it's name. Unfortunately we cannot use UseUrls extension method, instead we have to configure the port in UserKestrel method options.

Now when you spin up application, it will start accepting requests on a new port defined in the configuration file.

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.