Configure Blazor app from server

Published on Saturday, May 2, 2020

Blazor app configuration

Microsoft recently release Blazor WebAssembly 3.2.0 Release Candidate. This version includes configuration of an application by appsettings.json configuration file.

It works very nicely and simple. It is possible to add appsettings.json file to your Blazor project. You can also add appsettings.Environment.json file and configure different environments (Production, Development). And then you can read configuration in the Main method same way as in ASP.NET Core application. For example:

public static class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);
        var environment = builder.HostEnvironment;
        builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(environment.BaseAddress) });

        // This will create IConfigurationRoot that is the same interface
        // as used to configure ASP.NET Core applications.
        var configuration = builder.Configuration.Build();

        // It is possible to use standard methods to read configuration.
        var useHttpClient = configuration.GetValue<bool>("UseHttpClient");
        Startup.ConfigureServices(builder.Services, false, useHttpClient);

        builder.RootComponents.Add<App>("app");

        await builder.Build().RunAsync();
    }
}

And example of configuration file:

{
    "UseHttpClient": true
}

Configure from server application

At the moment Blazor WebAssembly supports only configuration from appsettings.json file by default. Good thing is that Microsoft.Extensions.Configuration library is pretty extensible and you can write your own configuration provider. However, in certain cases, there may be easier way.

In my case I don't want that appsettings.json file is static file, but that it is dynamically generated at server. And the reason for that is that I deploy my application in Docker container hosted in Azure App Service. And most of the configuration is provided via environment variables passed to the container. So the question is, how some of those variables can be passed to Blazor application configuration.

At first I created class that represents client-side configuration serializable in JSON.

public class PlanningPokerClientConfiguration
{
    public bool UseServerSide { get; set; }

    public bool UseHttpClient { get; set; }
}

Then I created configuration endpoint. It was implemented as ASP.NET Core controller that provides single route configuration. The controller was very simple. It just gets configuration object injected in constructor and then returns it from action method.

[ApiController]
[Route("[controller]")]
public class ConfigurationController : ControllerBase
{
    public ConfigurationController(PlanningPokerClientConfiguration clientConfiguration)
    {
        ClientConfiguration = clientConfiguration ?? throw new ArgumentNullException(nameof(clientConfiguration));
    }

    public PlanningPokerClientConfiguration ClientConfiguration { get; }

    [HttpGet]
    public ActionResult GetConfiguration()
    {
        return Ok(ClientConfiguration);
    }
}

Then I configured dependency injection in Startup class to include configuration.

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

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        // register services for dependency injection

        var clientConfiguration = GetPlanningPokerClientConfiguration();
        services.AddSingleton<PlanningPokerClientConfiguration>(clientConfiguration);
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // configure application
    }

    private PlanningPokerClientConfiguration GetPlanningPokerClientConfiguration()
    {
        return Configuration.GetSection("PlanningPokerClient").Get<PlanningPokerClientConfiguration>() ?? new PlanningPokerClientConfiguration();
    }
}

Unfortunately Blazor WebAssembly loads configuration only if appsettings.json file exists in wwwroot folder of Blazor application project. So I included empty JSON file in my Blazor wwwroot folder of my application project. This is required, because compiler then generates Blazor application bootstrap that includes reference to load the configuration file.

{
  // This JSON file should not be used. It is here only to force Blazor to load configuration.
}

And the last problem is that Blazor WebAssembly reads configuration from /appsettings.json URL and not from /configuration URL that refers to the controller I implemented. Luckily there is ASP.NET Core Rewrite middleware. This middleware can change URL of HTTP request in ASP.NET Core application before it is processed by rest of the pipeline.

In my case I simply want ASP.NET Core application to treat URL /appsettings.json as /configuration. So I added Rewrite middleware in Configure method of Startup class.

public class Startup
{
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // Rewrite middleware should be registered before other middlewares.
        var rewriteOptions = new RewriteOptions()
            .AddRewrite(@"^appsettings\.json$", "configuration", false);
        app.UseRewriter(rewriteOptions);

        // Register other middlewares
        app.UseStaticFiles();
        app.UseBlazorFrameworkFiles();

        app.UseRouting();

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

The first parameter of AddRewrite method is regular expression pattern, so it is possible to define dynamic patterns for URL rewriting.

Now I can configure Blazor client-side application by environment variables or command-line arguments at server.

# configure via command-line
dotnet .\Duracellko.PlanningPoker.Web.dll --PlanningPokerClient:UseHttpClient=true

# configure via environment variables
$env:PlanningPokerClient__UseHttpClient = 'true'
dotnet .\Duracellko.PlanningPoker.Web.dll

You can explore implementation in real application Planning Poker 4 Azure.

PS: Do not include any secrets (e.g. passowrds, security keys) in your Blazor application configuration. The configuration is loaded by client and thus anyone can read those valued with standard web browser developer tools.