ASP.NET Core: Application Settings in Razor

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();
    }
}