This article provides code that works for dotnet version 2.1.4. It important to note that as the API for accomplishing this seems to change with every release. I found that out the hard way.

While trying to show students how to get an environment variable, or configuration setting for their third-party API keys into JavaScript, I figured I'd quickly find out how to accomplish that in ASP.NET Core. Well, 2.5 hours later I was having a discussion with them about how this is what the life of a software developer looks like.

  1. Determine problem to solve
  2. Discover that the solution isn't obvious
  3. Google search
  4. Read 5 articles
  5. Realize they are all grotesquely outdated
  6. Find 1 or 2 promising ones
  7. Try the solutions
  8. Neither works
  9. Repeat 3-7 until 8 doesn't happen any more

After going through that process for the 875,201st time in my career, I'm letting others know my solution.

Create Settings

Open your appsettings.json file and add a configuration section. In the JSON below, I've added an ApplicationConfiguration object to the settings.

{
    "ConnectionStrings": {
      "DefaultConnection": "Data Source=MovieHistory.db"
    },
    "Logging": {
      "IncludeScopes": false,
      "LogLevel": {
        "Default": "Debug",
        "System": "Information",
        "Microsoft": "Information"
      }
    },
    "ApplicationConfiguration": {
      "MovieAPIKey": "9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f"
    }
  }

Create Service Interface

Next, you need to create a service which can be injected into the Razor template. First, create an interface. Here's my Services/IApplicationConfiguration.cs file.

namespace MovieHistory.Services
{
    public interface IApplicationConfiguration
    {
        /*
            Note that each property here needs to exactly match the 
            name of each property in my appsettings.json config object
        */
        string MovieAPIKey { get; set; }
    }
}

Next, you'll need an implementation of the service. Below is my Services/ApplicationConfiguration.cs file.

namespace MovieHistory.Services
{
    public class ApplicationConfiguration: IApplicationConfiguration
    {
        /*
            Note that each property here needs to exactly match the 
            name of each property in my appsettings.json config object
        */
        public string MovieAPIKey { get; set; }
    }
}

These two files create the base for a service. Now it's time to register the service in your application.

Register the Service

In Startup.cs, I use service.AddSingleton to register my service in the ConfigureServices method.

public void ConfigureServices(IServiceCollection services)
{
    ...

    /**
        Create a service for DI that will return the ApplicationConfiguration
        section of appsettings. This is just a factory function.
    */
    services.AddSingleton<IApplicationConfiguration, ApplicationConfiguration>(
        e => Configuration.GetSection("ApplicationConfiguration")
                .Get<ApplicationConfiguration>());

	services.AddMvc();
}

Inject the Service into Razor Template

Now I can use it in Views/Shared/_Layout.cshtml by using the @inject directive, and then accessing the MovieAPIKey property in the script tag.

@inject MovieHistory.Services.IApplicationConfiguration AppSettings;

<!DOCTYPE html>
<html>
	<head>...</head>

	<body>
	...

    <environment include="Development">
        <script type="text/javascript">
            const moviedb = Object.create(null, {
                "key": {
                    get: () => '@AppSettings.MovieAPIKey'
                }
            })
            Object.freeze(moviedb)
        </script>
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>

	</body>
</html>

Inject into Controller

I wanted the configuration setting to be used in the _Layout.cshtml so that it was available in every view, but if you have configuration that's only needed in a particular controller, or a particular method, you can use dependency injection to get the singleton that was configured in Startup.cs (see above) injected into a controller's constructor method.

public class HomeController : Controller
{
    private readonly IApplicationConfiguration _appSettings;

    public HomeController(IApplicationConfiguration appSettings)
    {
        _appSettings = appSettings;
    }

    public IActionResult Index()
    {
        ViewData["apiKey"] = _appSettings.MovieAPIKey;

        return View();
    }
}