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.
- Determine problem to solve
- Discover that the solution isn't obvious
- Google search
- Read 5 articles
- Realize they are all grotesquely outdated
- Find 1 or 2 promising ones
- Try the solutions
- Neither works
- 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();
    }
}