Quantcast
Channel: Andrew Lock | .NET Escapades
Viewing all articles
Browse latest Browse all 743

5 ways to set the URLs for an ASP.NET Core app

$
0
0
5 ways to set the URLs for an ASP.NET Core app

By default, ASP.NET Core apps listen on the following URLs:

In this post I show 5 different ways to change which URLs your app listens on.

There are multiple ways to set the URLs that ASP.NET Core binds to on startup. I have an old post about the various options available to you that applies to ASP.NET Core 1.0, but the options available in ASP.NET Core 3.x are much the same:

  • UseUrls() - Set the URLs to use statically in Program.cs.
  • Environment variables - Set the URLs using DOTNET_URLS or ASPNETCORE_URLS.
  • Command line arguments - Set the URLs with the --urls parameter when running from the command line.
  • Using launchSettings.json - Set the URLs using the applicationUrl property.
  • KestrelServerOptions.Listen() - Configure addresses for Kestrel server manually using Listen().

We'll look at each of these options in more detail below.

What URLs can you use?

In this post I describe the "URLs" you can bind to, but you can't use just any URL. There are essentially 3 classes of URLs that you can bind:

  • The "loopback" hostname for IPv4 and IPv6 (e.g. http://localhost:5000), in the format: {scheme}://{loopbackAddress}:{port}
  • A specific IP address available on your machine (e.g. http://192.168.8.31:5005), in the format {scheme}://{IPAddress}:{port}
  • "Any" IP address for a given port (e.g. http://*:6264), in the format {scheme}://*:{port}

The port in the above patterns is also optional - if you omit it, the default port for the given scheme is used instead (port 80 for http, port 443 for https).

Which of these pattern you choose will depend on your deployment mechanism. For example, if you're hosting multiple applications on a "bare metal" machine, you may well need to set an explicit IPAddress. If you're hosting in a container, then you can generally use a localhost address.

Watch out for the "any" IP address format - you don't have to use *, you can use anything that's not an IP Address and is not localhost. That means you can use http://*, http://+, http://mydomain, or http://example.org. All of these behave identically, and listen on any IP address. If you only want to handle requests from a single hostname, you need to configure host filtering in addition.

Once you know the URLs you need to listen on, you need to tell your application about them. In this post I show 5 possible ways of doing that.

UseUrls()

The first, and easiest, option to set the binding URLs is to hard code them when configuring the IWebHostBuilder using UseUrls():

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>();
                webBuilder.UseUrls("http://localhost:5003", "https://localhost:5004");
            });
}

Hard-coding the URLs never feels like a particularly clean or extensible solution, so this option isn't really useful for anything more than demos.

Luckily, you can also load the URLs from an external configuration file, from environment variables, or from command line arguments.

Environment variables

.NET Core uses two types of configuration:

  • App configuration is the configuration you typically use in your application, and is loaded from appSettings.json and environment variables, among other places.

  • Host configuration is used to configure basic things about your application, like the hosting environment and the host URLs to use.

The host configuration is the one we're interested in when considering how to set the URLs for our application. By default, host configuration values are loaded from three different sources:

  • Environment variables that have the prefix DOTNET_. The environment variables have the prefix removed and are added to the collection.
  • Command line arguments.
  • Environment variables that have the prefix ASPNETCORE_. For ASP.NET Core apps only, these environment variables are also added. These aren't added if you are creating a generic-host-based worker service.

If you don't override them manually with UseUrls(), then ASP.NET Core will use the value of the URLS key from the configuration system. Based on the description above you can set the URLS using either of the following environment variables:

  • DOTNET_URLS
  • ASPNETCORE_URLS

If you set both of these environment variables, the ASPNETCORE_URLS parameter takes precedence.

You can set environment variables in the usual way based on your environment. For example, using the command line:

setx ASPNETCORE_URLS "http://localhost:5001"

using powershell

$Env: ASPNETCORE_URLS = "http://localhost:5001"

or using bash:

export ASPNETCORE_URLS="http://localhost:5001;https://localhost:5002"

As you can see above, you can also pass multiple addresses to listen on (using HTTP or HTTPS) by separating them with a semicolon.

Command line arguments

The other way to set host configuration values is to use the command line. Command line arguments override the value of the environment variables if they're set. Simply use the --urls parameter:

dotnet run --urls "http://localhost:5100"

As before, you can pass multiple URLs to listen on by separating them with a semicolon:

dotnet run --urls "http://localhost:5100;https://localhost:5101"

Environment variables and command line arguments are probably the most common way to set URLs for an application in production, but they're a bit cumbersome for local development. It's often easier to using launchSettings.json instead.

launchSettings.json

Most .NET project templates include a launchSettings.json file in the Properties folder. This file contains various profiles for launching your ASP.NET Core application. A typical file contains one definition for launching the profile directly from the command line and one definition for launching the profile using IIS Express. This file drives the Debug drop-down in Visual Studio:

The launchsettings.json drives the Visual Studio Debug view

launchSettings.json provides an easy way to set the application URLs via the applicationUrl property - you can see one under the iisSettings for IIS express, and one under TestApp (the name of the application for this file).

{
  "iisSettings": {
    "windowsAuthentication": false, 
    "anonymousAuthentication": true, 
    "iisExpress": {
      "applicationUrl": "http://localhost:38327",
      "sslPort": 44310
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "TestApp": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "https://localhost:5001;http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

You don't need to do anything special to use this file — dotnet run will pick it up automatically.

launchSettings.json also provides an easy way to set additional environment variables using the environmentVariables, as you can see from the file above.

When you run your app from the command line with dotnet run, your app will use the applicationUrl properties in the "Project" command: https://localhost:5001;http://localhost:5000 in the file above. When you run the app using the "IISExpress" command, your app will use the applicationUrl from the iisSettings.iisExpress node: http://localhost:38327

This file is the easiest way to configure your environment when developing locally. In fact, you have to go out of your way to not use the launchSettings.json:

dotnet run --no-launch-profile

This will skip over the launchSettings.json file and fall back to the machine environment variables to determine the URLs instead.

All of the approaches shown so far set the URLs for Kestrel indirectly, but you can also set them directly.

KestrelServerOptions.Listen()

Kestrel is configured by default in almost all ASP.NET Core apps. If you wish, you can configure the endpoints for Kestrel manually, or via configuring KestrelServerOptions using the IConfiguration system.

I've never found myself actually needing to do this, and there's a lot of configuration options available, so for the most part I suggest referring to the documentation. As an example, you can use the Listen() functions exposed by KestrelServerOptions:

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>();
                webBuilder.UseKestrel(opts =>
                {
                    // Bind directly to a socket handle or Unix socket
                    // opts.ListenHandle(123554);
                    // opts.ListenUnixSocket("/tmp/kestrel-test.sock");
                    opts.Listen(IPAddress.Loopback, port: 5002);
                    opts.ListenAnyIP(5003);
                    opts.ListenLocalhost(5004, opts => opts.UseHttps());
                    opts.ListenLocalhost(5005, opts => opts.UseHttps());
                });

            });
}

This configuration sets Kestrel listening on multiple addresses. It's hard-coded in the example above, but it doesn't have to be — you can bind to an IConfiguration instead. When you set the URLs for kestrel in this way, it overrides the URLS configuration value if you've set it through one of the other mechanisms as well, such as environment variables. You'll see a warning in the logs if that happens:

warn: Microsoft.AspNetCore.Server.Kestrel[0]
      Overriding address(es) 'http://localhost:5007'. Binding to endpoints defined in UseKestrel() instead.
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://127.0.0.1:5002 
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://[::]:5003

Personally I haven't found a need to set the listening endpoints in Kestrel this way, but it's good to be aware that you can get complete control of Kestrel like this if you need it.

Summary

In this post I showed five different ways you can set the URLs that your application listens on. UseUrls() is one of the simplest, but generally isn't suitable for production workloads. The --urls command line argument and ASPNETCORE_/DOTNET_ environment variables are most useful for setting the values in production. The launchSettings.json file is very useful for setting the URLs in a development environment. If you need fine-grained control over your configuration, you can use Kestrel's Listen* options directly. These can also be loaded from configuration for easy use in both production and development.


Viewing all articles
Browse latest Browse all 743

Trending Articles