ASP.NET Core Configuration

Configuration is the cornerstone of any application, providing essential settings and values that drive its behavior. ASP.NET Core's configuration system is flexible and extensible, allowing you to retrieve configuration data from various sources and prioritize them according to your needs.
Core Concepts
Configuration Providers: These components read configuration data from different sources and populate a central configuration store.
Configuration Sources: The actual locations or mechanisms where your configuration data resides (e.g., files, environment variables, command-line arguments).
Key-Value Pairs: Configuration data is stored as key-value pairs, where the key is a string identifier, and the value is the configuration data (string, number, boolean, etc.).
Common Configuration Sources
Files (JSON, XML, INI):
Purpose: Storing configuration data in structured files. JSON is the default and most common format in ASP.NET Core.
Pros: Easy to read and edit, supports hierarchical structure.
Cons: Might not be suitable for storing secrets or highly sensitive data.
Environment Variables:
Purpose: Reading configuration values from environment variables.
Pros: Ideal for environment-specific settings (e.g., database connection strings) and secrets.
Cons: Can be difficult to manage for complex configurations or large numbers of settings.
Command-Line Arguments:
Purpose: Overriding configuration values when running the application from the command line.
Pros: Provides flexibility for dynamic configuration on the fly.
Cons: Might not be suitable for storing complex or sensitive data.
In-Memory .NET Objects:
Purpose: Storing configuration data in a dictionary or custom objects directly in your code.
Pros: Flexibility for dynamic or programmatic configuration scenarios.
Cons: Not persistent, less suitable for managing a large number of settings.
Azure Key Vault:
Purpose: Securely storing secrets and sensitive configuration data in the cloud.
Pros: Highly secure, centralized management of secrets.
Cons: Requires Azure subscription and setup.
Azure App Configuration:
Purpose: A powerful cloud-based service for managing feature flags and configuration settings.
Pros: Feature flag management, centralized configuration, dynamic updates.
Cons: Requires Azure subscription and setup.
User Secrets (Development):
Purpose: Storing sensitive data (e.g., API keys) during development without committing them to source control.
Pros: Secure and convenient for local development.
Cons: Not intended for production environments.
Adding and Managing Configuration Sources in Program.cs
var builder = WebApplication.CreateBuilder(args);var configuration = builder.Configuration; // Add configuration sources in the desired order of precedence (last added wins)configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);configuration.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);configuration.AddEnvironmentVariables();configuration.AddUserSecrets<Program>(); // For development secrets// ... other sources ...
AddJsonFile
: Loads configuration from JSON files.AddEnvironmentVariables
: Loads configuration from environment variables.AddUserSecrets<Program>()
: Loads configuration from the user secrets store (for development).
When to Use Which Configuration Source
appsettings.json: For default settings, base configurations, non-sensitive data.
appsettings.{Environment}.json: For environment-specific overrides.
Environment Variables: For environment-specific settings, sensitive data (API keys, connection strings).
Command-Line Arguments: For overriding settings during development or deployment.
User Secrets: For sensitive data during local development.
Azure Key Vault: For storing secrets and other sensitive data securely in production.
Azure App Configuration: For dynamic configuration updates, feature flags, and centralized management.
Best Practices
Layered Configuration: Use multiple sources with a well-defined order of precedence to keep your configuration organized and flexible.
Environment-Specific Settings: Separate sensitive and environment-specific settings into appropriate files.
Secrets Management: Use Azure Key Vault or other secure mechanisms to store sensitive data.
Strong Typing: Create strongly typed configuration classes using the Options pattern (
IOptions<T>
) for improved type safety and easier access to your settings in code.Validation: Validate your configuration values during startup to catch errors early.
Logging: Log configuration-related events to help with troubleshooting and debugging.
IConfiguration
In ASP.NET Core, the IConfiguration
interface is the heart of the configuration system. It represents a set of key-value pairs that can be loaded from various sources (JSON files, environment variables, etc.). This interface provides a unified way to access your application's settings, regardless of where they are stored.
Key Methods, Properties, and Indexers
GetSection(string key):
Purpose: Retrieves a specific section of the configuration as an IConfigurationSection. Sections allow you to group related settings.
Example:
var connectionStrings = configuration.GetSection("ConnectionStrings");
GetValue<T>(string key):
Purpose: Retrieves a configuration value as a specified type
T
.Example:
var port = configuration.GetValue<int>("Server:Port");
GetConnectionString(string name):
Purpose: Retrieves a connection string from the "ConnectionStrings" section of the configuration.
Example:
var connectionString = configuration.GetConnectionString("DefaultConnection");
GetChildren():
Purpose: Returns an enumerable collection of IConfigurationSection objects representing the immediate children of the current section.
Example:
var sections = configuration.GetSection("Logging").GetChildren();
Indexer (this[string key]):
Purpose: Retrieves a configuration value as a string.
Example:
var value = configuration["Logging:LogLevel:Default"];
Injecting IConfiguration
- In Controllers:
public class HomeController : Controller{ private readonly IConfiguration _configuration; // Field to store IConfiguration public HomeController(IConfiguration configuration) { _configuration = configuration; } public IActionResult Index() { var myKeyValue = _configuration["MyKey"]; // Access configuration value return View(); }}
- In Services:
public class EmailService : IEmailService{ private readonly IConfiguration _configuration; public EmailService(IConfiguration configuration) { _configuration = configuration; } public void SendEmail(string to, string subject, string body) { var smtpServer = _configuration["Email:SmtpServer"]; // Use configuration for email settings // ... (email sending logic) }}
In both cases, the IConfiguration is injected through the constructor using ASP.NET Core's dependency injection.
Best Practices
Strongly Typed Configuration: Use the Options pattern (
IOptions<T>
) to map your configuration values to strongly typed objects for easier access and type safety.Environment-Specific Settings: Use
appsettings.{Environment}.json
files to store configuration values that vary depending on the environment (Development, Production, etc.).Secret Management: Store sensitive information (e.g., passwords, API keys) in Azure Key Vault or other secure storage mechanisms.
Layered Configuration: Combine multiple configuration sources (files, environment variables, etc.) with a well-defined order of precedence.
Reload On Change: Consider using
reloadOnChange: true
in your configuration providers to automatically reload configuration changes without restarting the application.
Example: Options Pattern
// MyOptions.cspublic class MyOptions{ public string Option1 { get; set; } public int Option2 { get; set; }} // Program.cs (or Startup.cs)builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("MyOptions")); // MyService.cspublic class MyService : IMyService{ private readonly IOptions<MyOptions> _options; public MyService(IOptions<MyOptions> options) { _options = options; } public void DoSomething() { var option1Value = _options.Value.Option1; // ... }}
In this example, the MyOptions
class represents a section of your configuration. The IOptions<MyOptions>
interface provides a strongly typed way to access those settings within your services.
By following these best practices and leveraging the power of IConfiguration
, you can build robust and adaptable ASP.NET Core applications with well-organized and easily manageable configuration settings.
Hierarchical Configuration
In ASP.NET Core, you can organize your configuration settings into a hierarchical structure using JSON, XML, or INI files. This hierarchical structure allows you to group related settings under sections and subsections, making your configuration more readable, maintainable, and scalable.
JSON-Based Hierarchical Configuration (appsettings.json):
{ "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "Inventory": { "StockAlertThreshold": 20, "WarehouseLocations": [ "New York", "London", "Tokyo" ] }}
In this example:
Sections: The top-level keys (ConnectionStrings, Logging, Inventory) define sections within the configuration.
Nested Sections: The Logging section further contains a nested LogLevel section.
Arrays: The WarehouseLocations setting is an array of strings within the Inventory section.
Accessing Hierarchical Configuration with IConfiguration
The IConfiguration interface provides methods to easily navigate and retrieve values from this hierarchical structure.
GetSection(string key):
Returns an IConfigurationSection object representing the specified section.
Use this to drill down into nested sections.
GetValue<T>(string key):
Retrieves a configuration value as the specified type
T
.The key can include the entire path to the value, using colons (
:
) to separate sections.
Indexer (this[string key]):
Retrieves a configuration value as a string.
Works like the GetValue<string>() method.
Code Examples
var connectionString = _configuration.GetConnectionString("DefaultConnection"); var logLevel = _configuration.GetValue<string>("Logging:LogLevel:Default"); // Using IConfigurationSection:var inventorySection = _configuration.GetSection("Inventory");var stockAlertThreshold = inventorySection.GetValue<int>("StockAlertThreshold"); // Get an arrayvar warehouseLocations = inventorySection.GetSection("WarehouseLocations").Get<string[]>();
Best Practices
Clear Structure: Organize your settings into logical sections and subsections for better readability and maintainability.
Consistent Naming: Use meaningful and consistent naming conventions for your configuration keys.
Strong Typing with Options Pattern: Use the Options pattern (IOptions<T>) to map your configuration sections to strongly typed classes, which provides type safety and makes your code easier to work with.
Environment Variables: Consider using environment variables for settings that may vary across environments (e.g., ASPNETCORE_ENVIRONMENT).
Secret Management: Never store sensitive information (passwords, API keys) directly in configuration files. Use Azure Key Vault, Secret Manager, or other secure mechanisms to manage secrets.
Example: Options Pattern
// InventoryOptions.cspublic class InventoryOptions{ public int StockAlertThreshold { get; set; } public string[] WarehouseLocations { get; set; }} // Program.cs (or Startup.cs)builder.Services.Configure<InventoryOptions>(builder.Configuration.GetSection("Inventory")); // In your service or controllerpublic class InventoryService : IInventoryService{ private readonly InventoryOptions _options; public InventoryService(IOptions<InventoryOptions> options) { _options = options.Value; } // ... use _options.StockAlertThreshold and _options.WarehouseLocations}
Options Pattern
The Options pattern is a design pattern in ASP.NET Core that enables you to access configuration values in a strongly typed manner. Instead of retrieving configuration values as strings and manually converting them to the appropriate types, you define POCO (Plain Old CLR Object) classes that represent the structure of your configuration sections. These classes, known as "options" classes, make your configuration code more readable, maintainable, and less error-prone.
Benefits of the Options Pattern
Strongly Typed Access: Access your configuration values directly as properties of your options classes, eliminating the need for manual type conversions and reducing the risk of runtime errors.
IntelliSense Support: Get code completion and type checking in your IDE when working with your configuration settings.
Validation: You can easily add validation logic to your options classes to ensure that configuration values are valid.
Clean Separation: Keep your configuration settings separate from your business logic, improving the overall organization of your code.
When to Use the Options Pattern
Related Settings: When you have groups of related configuration settings that logically belong together (e.g., database connection settings, email settings, feature flags).
Strongly Typed Access: When you want to work with your configuration values in a type-safe manner.
Validation: When you want to add validation logic to ensure your configuration values are valid.
How to Implement the Options Pattern
- Create an Options Class: Define a class that mirrors the structure of your configuration section. Make sure the property names match the keys in your configuration file.
public class EmailOptions{ public string SmtpServer { get; set; } = string.Empty; public int SmtpPort { get; set; } = 25; public string SenderEmail { get; set; } = string.Empty; public string SenderPassword { get; set; } = string.Empty;}
- Register the Options: In your Program.cs (or Startup.cs in older versions), register your options class using the
Configure<T>
extension method onIServiceCollection
:
builder.Services.Configure<EmailOptions>(builder.Configuration.GetSection("Email"));
This tells the DI container to bind the settings in the Email
section of your configuration to an instance of EmailOptions
.
- Inject
IOptions<T>
: Inject theIOptions<T>
interface into your controllers or services to access the bound options:
public class EmailService : IEmailService{ private readonly EmailOptions _options; public EmailService(IOptions<EmailOptions> options) { _options = options.Value; } // ... use _options.SmtpServer, _options.SmtpPort, etc. ...}
Related Methods for Configuration Access
ConfigurationBinder.Get<T>(IConfiguration configuration)
: Binds and returns the entire configuration section to a strongly typed object of typeT
.ConfigurationBinder.Get(IConfiguration configuration, Type type)
: Binds and returns the entire configuration section to an object of the specified type.ConfigurationBinder.Bind(IConfiguration configuration, object instance)
: Binds the configuration to an existing object instance.
Example: Options Pattern with GetSection and Bind
// Program.cs (or Startup.cs)var emailOptions = new EmailOptions();builder.Configuration.GetSection("Email").Bind(emailOptions); builder.Services.AddSingleton(emailOptions); // Add the bound object as a singleton
Environment-Specific Configuration Files
ASP.NET Core allows you to create configuration files that are specific to different environments. By convention, these files are named appsettings.{Environment}.json
, where {Environment}
is replaced with the name of the environment (e.g., appsettings.Development.json
, appsettings.Production.json
).
Purpose:
Environment-Specific Settings: These files store configuration values that are unique to each environment. This could include database connection strings, API keys, logging levels, or feature flags.
Customization: You can tailor your application's behavior for development, testing, staging, and production environments without having to manually modify configuration settings every time you deploy.
Order of Precedence:
ASP.NET Core loads configuration from multiple sources, and the order in which they are loaded determines which values take precedence in case of conflicts. The general order of precedence (from highest to lowest) is:
Command-Line Arguments: Any configuration values specified as command-line arguments when you run your application (e.g.,
dotnet run --Logging:LogLevel:Default=Debug
) override all other sources.Environment Variables: Configuration values set as environment variables on your system take precedence over values in configuration files. ASP.NET Core automatically maps environment variables to configuration keys using a convention. For example, the environment variable
ConnectionStrings__DefaultConnection
would map to the configuration keyConnectionStrings:DefaultConnection
.User Secrets (Development Only): If you're in the
Development
environment, values from the user secrets store (secrets.json
) override those fromappsettings.json
andappsettings.Development.json
. This is useful for storing sensitive information during development.appsettings.{Environment}.json: If present, settings from this file override values from the base
appsettings.json
file. This allows you to customize settings for specific environments.appsettings.json: This is the base configuration file that is always loaded. It contains the default settings for your application.
Example: Overriding Connection Strings
// appsettings.json{ "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDatabaseDev;Trusted_Connection=True;" }} // appsettings.Production.json{ "ConnectionStrings": { "DefaultConnection": "Server=myprodserver;Database=MyDatabaseProd;User Id=myuser;Password=mypassword;" }}
If the ASPNETCORE_ENVIRONMENT
variable is set to "Production", the connection string from appsettings.Production.json
will be used.
Code Example: GetSection() and GetValue()
var connectionString = _configuration.GetConnectionString("DefaultConnection"); var logLevel = _configuration.GetValue<string>("Logging:LogLevel:Default");
GetConnectionString("DefaultConnection")
is a convenience method to fetch a connection string specifically from theConnectionStrings
section.GetValue<string>()
retrieves values from specific configuration sections or keys.
Best Practices
Logical Structure: Organize your settings into sections and subsections to make your configuration files easy to read and understand.
Consistent Naming: Use consistent naming conventions for your configuration keys (e.g., kebab-case, snake_case).
Environment Variables for Sensitive Data: Store sensitive information like API keys and connection strings in environment variables or Azure Key Vault, not in configuration files that might be committed to source control.
User Secrets for Development: Use user secrets to store sensitive data during development without exposing it in your code repository.
Order Matters: Be mindful of the order of precedence when adding configuration sources. Place the most important or specific overrides later in the process.
Validation: Consider validating your configuration during application startup to ensure that all required settings are present and have valid values.
Secrets Management in ASP.NET Core
In the world of web development, you'll often need to work with sensitive information like API keys, database connection strings, or passwords. Hardcoding these values directly into your source code is a security risk. That's where Secrets Manager comes into play.
Secrets Manager: Your Digital Vault
Secrets Manager is a tool that provides secure storage and management for your application's secrets. It keeps your sensitive data out of your source code and makes it easier to manage and rotate secrets without redeploying your application.
User Secrets: Keeping Development Secrets Safe
User Secrets is a developer-friendly feature of Secrets Manager specifically designed for local development environments. It allows you to store secrets for a particular project on your local machine without having to commit them to source control, keeping them out of your code repository.
How to Set User Secrets Using the dotnet Command
Initialize: If you haven't already, initialize user secrets for your project:
dotnet user-secrets init
This command adds a UserSecretsId property to your project's .csproj file, which links the project to a user secrets store.
Set a Secret: Use the set command to store a secret:
dotnet user-secrets set "MySecretName" "MySecretValue"
Replace
"MySecretName"
with the desired key and"MySecretValue"
with the actual secret value.List Secrets (Optional):
dotnet user-secrets list
This command lists all the secrets you've stored for the project.
Remove a Secret (Optional):
dotnet user-secrets remove "MySecretName"
Accessing User Secrets in Your Code
var builder = WebApplication.CreateBuilder(args);var configuration = builder.Configuration; // In Program.cs (or Startup.cs):if (builder.Environment.IsDevelopment()){ configuration.AddUserSecrets<Program>();}
This will add a configuration source that can read user secrets, but only when the environment is set to "Development".
Then, to access a user secret, you can use the same techniques you would for any other configuration value:
var mySecret = configuration["MySecretName"];
Best Practices for Secrets Management
Never Hardcode Secrets: Always store sensitive information in a secure store like Secrets Manager.
Least Privilege: Grant your application the minimum necessary permissions to access secrets.
Rotate Secrets Regularly: Regularly change your secrets to minimize the risk of exposure.
Separate Environments: Use different secrets for different environments (development, staging, production).
Automation: Consider automating the process of secret rotation to enhance security.
Example: Storing an API Key as a User Secret
Initialize:
dotnet user-secrets init
Set Secret:
dotnet user-secrets set "StripeApiKey" "sk_test_1234567890"
Accessing in Your Code (Example):
var stripeApiKey = configuration["StripeApiKey"];
Caveats
Development Only: User secrets are intended for development environments and should not be used in production.
Local Storage: User secrets are stored in a JSON file on your local machine. Ensure this file is protected.
Set Configuration Values from Environment Variables
Flexibility: You can dynamically change your application's settings without modifying code or configuration files.
Security: Environment variables are a secure way to store sensitive information like API keys, connection strings, or passwords without embedding them in your code.
Deployment Environments: Different environments (development, staging, production) often require distinct configuration values. Environment variables can be easily set and managed per environment.
Automation: This approach lends itself well to automation scripts for deployment and configuration.
How It Works
Environment Variable Prefix: ASP.NET Core's configuration system recognizes environment variables that start with a specific prefix, by default,
ASPNETCORE_
. This allows you to namespace your environment variables to avoid conflicts with other variables on your system.Key Mapping: The part of the environment variable name after the prefix is used as the configuration key. For example, the environment variable
ASPNETCORE_Logging__LogLevel__Default
will map to the configuration keyLogging:LogLevel:Default
. Double underscores (__
) are used to represent colons (:
) in the hierarchy.Configuration Provider: ASP.NET Core has a built-in configuration provider called
EnvironmentVariablesConfigurationProvider
that automatically reads these environment variables and adds them to the configuration system.
Setting Environment Variables from the Command Line
PowerShell (Windows, macOS, Linux)
$env:ASPNETCORE_MyKey = "myvalue" # Simple key-value$env:ASPNETCORE_Logging__LogLevel__Default = "Debug" # Hierarchical key
In PowerShell, use the $env:
prefix to set environment variables within the current session.
Command Prompt (Windows)
set ASPNETCORE_MyKey=myvalue # Simple key-valueset ASPNETCORE_Logging__LogLevel__Default=Debug # Hierarchical key
Bash (macOS, Linux)
export ASPNETCORE_MyKey="myvalue" # Simple key-valueexport ASPNETCORE_Logging__LogLevel__Default="Debug" # Hierarchical key
Example: Setting a Database Connection String
Let's say you want to set your database connection string using an environment variable. Here's how you would do it:
Set the Environment Variable:
# In PowerShell$env:ASPNETCORE_ConnectionStrings__DefaultConnection = "Server=myServer;Database=myDb;Trusted_Connection=True;" # In Command Prompt (Windows)set ASPNETCORE_ConnectionStrings__DefaultConnection="Server=myServer;Database=myDb;Trusted_Connection=True;" # In Bash (macOS/Linux)export ASPNETCORE_ConnectionStrings__DefaultConnection="Server=myServer;Database=myDb;Trusted_Connection=True;"
Note the double underscores (__
) used to represent the colon (:
) in the configuration path.
- Access in Your Code: You can then retrieve this connection string in your ASP.NET Core application using:
var connectionString = _configuration.GetConnectionString("DefaultConnection");
Notes
Prefix: Remember to use the
ASPNETCORE_
prefix for your environment variables.Key Mapping: Double underscores (
__
) in the environment variable name are translated to colons (:
) in the configuration key.Override: Environment variable values will override those set in
appsettings.json
orappsettings.{Environment}.json
.Sensitive Data: This is an excellent way to manage sensitive data without exposing it in your code or configuration files.
Deployment: Make sure to set the appropriate environment variables on your production server before deploying your application.
The Mechanics of Environment Variable Configuration
Environment Variable Prefix: ASP.NET Core's configuration system recognizes environment variables that start with a specific prefix. By default, this prefix is
ASPNETCORE_
. You can customize this prefix if needed. This prefix helps to namespace your environment variables and avoid conflicts with other variables on your system.Key Mapping: The part of the environment variable name after the prefix is used as the configuration key. A double underscore (
__
) is used to represent a colon (:
) in the hierarchical structure of your configuration. For example:Environment Variable:
ASPNETCORE_Logging__LogLevel__Default
Configuration Key:
Logging:LogLevel:Default
Configuration Provider: ASP.NET Core includes a built-in configuration provider called
EnvironmentVariablesConfigurationProvider
. This provider automatically reads environment variables that match the prefix and adds them to the application's configuration. The values from environment variables override any matching values found inappsettings.json
or environment-specific configuration files.
Setting Environment Variables from the Command Line
PowerShell (Windows, macOS, Linux)
$env:ASPNETCORE_MyKey = "myvalue" # Simple key-value$env:ASPNETCORE_Logging__LogLevel__Default = "Debug" # Hierarchical key
Command Prompt (Windows)
set ASPNETCORE_MyKey=myvalue # Simple key-valueset ASPNETCORE_Logging__LogLevel__Default=Debug # Hierarchical key
Bash (macOS, Linux)
export ASPNETCORE_MyKey="myvalue" # Simple key-valueexport ASPNETCORE_Logging__LogLevel__Default="Debug" # Hierarchical key
Example: Setting a Database Connection String
Let's say you want to set your database connection string using an environment variable. Here's how you would do it:
Set the Environment Variable:
# In PowerShell or Bash$env:ASPNETCORE_ConnectionStrings__DefaultConnection = "Server=myServer;Database=myDb;User Id=myuser;Password=mypassword;" # In Command Prompt (Windows)set ASPNETCORE_ConnectionStrings__DefaultConnection="Server=myServer;Database=myDb;User Id=myuser;Password=mypassword;"
Access in Your Code: You can then retrieve this connection string in your ASP.NET Core application as usual:
var connectionString = _configuration.GetConnectionString("DefaultConnection");
Important Considerations
Prefix Customization: You can change the default
ASPNETCORE_
prefix using theAddEnvironmentVariables
method. For example,configuration.AddEnvironmentVariables("CUSTOM_PREFIX_");
Case Sensitivity: On Linux and macOS, environment variable names are case-sensitive.
Deployment: When deploying your application, ensure that the appropriate environment variables are set on the target server.
Security: While environment variables are more secure than hardcoding values, they might not be suitable for extremely sensitive secrets. In those cases, consider using a dedicated secret management solution like Azure Key Vault or HashiCorp Vault.
Custom JSON Files
While ASP.NET Core natively supports appsettings.json
and environment-specific variations, there are scenarios where using custom JSON files for configuration might be advantageous:
Modularity: You can organize settings into multiple files based on functional areas or components, making your configuration more manageable and easier to navigate.
Customization: You can load custom JSON files conditionally, based on specific requirements or runtime decisions.
Separation of Concerns: This approach allows you to keep default settings in
appsettings.json
while maintaining custom settings separately.
Adding Custom JSON Files as Configuration Sources
- Create the File: Create a JSON file with your custom configuration settings. Let's call it
customsettings.json
:
{ "CustomSettings": { "APIKey": "your_api_key", "FeatureEnabled": true, "NotificationSettings": { "EmailEnabled": true, "SMSEnabled": false } }}
- Add to Configuration: In your
Program.cs
, use theAddJsonFile
extension method to include your custom JSON file:
var builder = WebApplication.CreateBuilder(args);var configuration = builder.Configuration; // ... (other configuration sources) ... // Add the custom JSON file:configuration.AddJsonFile("customsettings.json", optional: true, reloadOnChange: true); var app = builder.Build();// ... (rest of the application) ...
optional: true
: Set this totrue
if the file might not exist (e.g., in certain environments).reloadOnChange: true
: Enables automatic reloading of the configuration if the file changes.
Accessing Custom Configuration Values
You can access values from your custom JSON file using the same mechanisms as you would for appsettings.json
:
// Option 1: Directly using IConfigurationvar apiKey = configuration["CustomSettings:APIKey"];var featureEnabled = configuration.GetValue<bool>("CustomSettings:FeatureEnabled"); // Option 2: Options Patternvar notificationSettings = configuration.GetSection("CustomSettings:NotificationSettings").Get<NotificationSettings>();
Best Practices
Naming: Choose descriptive and meaningful names for your custom JSON files.
Organization: Structure your custom configuration files with sections and subsections to enhance readability and maintainability.
Environment-Specific Overlays: Create environment-specific versions of your custom files (e.g.,
customsettings.Development.json
) to override settings in different environments.Secrets Management: Store sensitive information (API keys, passwords) in a secure store like Azure Key Vault or User Secrets.
Error Handling: Handle potential errors, such as missing or invalid configuration files, gracefully.
Strong Typing with Options: Strongly recommend using Options Pattern for type safety and better code structure.
Example: Options Pattern with Custom JSON File
// CustomSettings.cs (Options Class)public class CustomSettings{ public string APIKey { get; set; } public bool FeatureEnabled { get; set; } public NotificationSettings NotificationSettings { get; set; }} // ... (other options classes if needed) ... // Program.csbuilder.Services.Configure<CustomSettings>(configuration.GetSection("CustomSettings")); // In your controller or servicepublic class MyController : Controller{ private readonly CustomSettings _settings; public MyController(IOptions<CustomSettings> settings) { _settings = settings.Value; }}
HttpClient
The HttpClient class is a powerful and versatile tool in the .NET ecosystem for interacting with web-based resources over the HTTP protocol. You use it to send requests (GET, POST, PUT, DELETE, etc.) to APIs and retrieve responses containing data in various formats (JSON, XML, HTML).
Key Features of HttpClient
Sending Requests: Craft and send HTTP requests to any URL.
Receiving Responses: Process the server's response (status code, headers, body content).
Async Operations: Designed for asynchronous programming, allowing your application to perform other tasks while waiting for network responses.
Customization: Configure request headers, timeouts, authentication, and more.
Using HttpClient in ASP.NET Core
While you can create and manage HttpClient
instances directly, ASP.NET Core offers a more robust approach through the IHttpClientFactory
interface. The factory handles the following for you:
Connection Pooling: Manages a pool of HTTP connections, optimizing performance and preventing socket exhaustion.
Lifetime Management: Ensures proper disposal of
HttpClient
instances to avoid resource leaks.Named Clients: Lets you define and configure named clients for different APIs, each with its own settings (base address, headers, etc.).
Integrating HttpClient with Your Stock App
Let's analyze how your stock application uses HttpClient and IHttpClientFactory:
FinnhubService:
Injection: The constructor injects
IHttpClientFactory
to createHttpClient
instances.Request Building: The
GetStockPriceQuote
method constructs anHttpRequestMessage
object, specifying the URL (including the Finnhub API token) and the HTTP method (GET).Sending the Request: It uses
httpClient.SendAsync
to send the request asynchronously.Response Processing: It reads the response content as a stream and deserializes the JSON data into a dictionary.
Error Handling: It checks for errors in the response and throws exceptions accordingly.
HomeController:
Injection: It injects both the
FinnhubService
andIOptions<TradingOptions>
for configuration.Data Fetching: The
Index
action calls_finnhubService.GetStockPriceQuote
to get stock data.Model Creation: It maps the retrieved data to a
Stock
model object.View Rendering: The
Stock
model is passed to the view for display.
Code Breakdown
IFinnhubService
: Defines an interface for the Finnhub service, allowing for different implementations if needed.FinnhubService
: Implements the interface and usesHttpClient
to interact with the Finnhub API.TradingOptions
: A class to hold configuration options for the default stock symbol (read from appsettings.json).Stock
: A model class to represent the stock data.HomeController
: The controller that fetches stock data and renders the view.
Best Practices
IHttpClientFactory
: Always useIHttpClientFactory
instead of directly creatingHttpClient
instances to benefit from connection pooling and proper lifetime management.Named Clients: For multiple APIs, use named clients (
_httpClientFactory.CreateClient("name");
) to configure different settings for each API.Error Handling: Handle exceptions that might occur during HTTP requests, such as network errors or invalid responses.
Resilience: Consider using Polly or other libraries to implement retries and circuit breaker patterns for increased resilience in the face of transient errors.
Key Points to Remember
Purpose: Provide named configurations to tailor your app's behavior for different scenarios (development, staging, production, etc.).
Environment Variable:
ASPNETCORE_ENVIRONMENT
is the key environment variable.Its value determines the active environment.
Setting the Environment:
launchSettings.json
(Development): Set within theenvironmentVariables
section of a profile.System Environment Variables: Set directly on your machine (persistent).
Command Line: Use
--environment
or-e
flag when running the app (e.g.,dotnet run --environment Staging
).Azure App Service: In the Azure portal, under Configuration > Application settings.
IWebHostEnvironment
Interface:Use it in your code to access environment information (e.g.,
EnvironmentName
,WebRootPath
).Inject it into your controllers or middleware:
private readonly IWebHostEnvironment _env; public MyController(IWebHostEnvironment env){ _env = env;}
Environment-Specific Configuration:
Create files like
appsettings.Development.json
,appsettings.Staging.json
, etc.ASP.NET Core automatically loads the appropriate file based on the environment.
Override base settings in
appsettings.json
.
Conditional Configuration (In
Program.cs
):- Use
if (app.Environment.IsDevelopment())
or similar methods to apply settings or middleware based on the environment.
- Use
if (app.Environment.IsDevelopment()){ app.UseDeveloperExceptionPage(); }
Default Environments:
Development
: Default for local development.Staging
: Typically used for pre-production testing.Production
: The live environment.
Custom Environments: You can define and use your own environment names.
Best Practices:
Separate Configurations: Keep environment-specific settings in separate files.
Tailor Middleware: Use different middleware pipelines for different environments (e.g., enable
DeveloperExceptionPage
only in development).Logging: Adjust logging levels based on the environment.
Feature Flags: Use environment variables to toggle features on/off.
Interview Tips
Explain the Why: Be able to articulate the reasons for using environments (configuration, security, flexibility).
Configuration: Show how you would use
appsettings.{Environment}.json
files to manage environment-specific settings.Middleware: Explain how you would customize middleware pipelines based on the environment.
Deployment: Discuss how you would set the environment variable when deploying to different servers.
part2:
Configuration Settings
Configuration (or configuration settings) are the constant key/value pairs that are set at a common location and can be read from anywhere in the same application.
Examples: connection strings, Client ID & API keys to make REST-API calls, Domain names, Constant email addresses etc.
Configuration Sources
appsettings.json
Environment Variables
File Configuration (JSON, INI or XML files)
In-Memory Configuration
Secret Manager
Access Configuration
in Program.cs:
app.Configuration
IConfiguration
[string key]
Gets or sets configuration value at the specified key.
GetValue<T>(string key, object defaultValue)
Gets the configuration value at the specified key; returns the default value if the key doesn't exists.
IConfiguration in Controller
in Controller and other classes
using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Configuration; public class ControllerName : Controller{ private readonly IConfiguration _configuration; public ControllerName(IConfiguration configuration) { _configuration = configuration; }}
Hierarchical Configuration
in appsettings.json
{ "MasterKey": { "Key1": "value" "Key2": "value" }}
to read configuration
Configuration["MasterKey:Key1"]
IConfiguration.GetSection(string key)
Returns an IConfigurationSection based on the specified key.
Options Pattern
Options pattern uses custom classes to specify what configuration settings are to be loaded into properties.
Examples: Reading the specific connections strings out of many configuration settings.
The option class should be a non-abstract class with a public parameterless constructor.
Public read-write properties are bound.
Fields are not bound.
IConfiguration.GetSection(string key)
Returns an IConfigurationSection based on the specified key.
IConfiguration.Bind(object instance) and IConfiguration.Get<T>()
Binds (loads) configuration key/value pairs into a new object of the specified type.
Configuration as Service
Inject Configuration as Service
Add Configuration as Service
in Program.cs:
builder.Services
.Configure<Model>(builder.Configuration.GetSection("MasterKey"));
Inject Configuration as Service in Controller in Controller and other classes
using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Options; public class ControllerName : Controller{ private readonly Model _options; public ControllerName(IOptions<Model> options) { _options = options.Value; }}
Environment Specific Configuration
Order of Precedence of Configuration Sources
Secrets Manager
The 'secrets manager ' stores the user secrets (sensitive configuration data) in a separate location on the developer machine.
Enable Secrets Manager in "Windows PowerShell" / "Developer PowerShell in VS"
dotnet user-secrets initdotnet user-secrets set "Key" "Value"dotnet user-secrets list
Environment Variables Configuration
You can set configuration values as in-process environment variables.
Set Configuration as Environment Variables
in "Windows PowerShell" / "Developer PowerShell in VS":
$Env:ParentKey__ChildKey="value"dotnet run --no-launch-profile
It is one of the most secured way of setting-up sensitive values in configuration.
__ (underscore and underscore) is the separator between parent key and child key.
Custom Json Configuration
Add Custom Json file as Configuration Source
in Program.cs:
builder.Host.ConfigureAppConfiguration( (hostingContext, config) => { config.AddJsonFile("filename.json", optional: true, reloadOnChange: true);});
Http Client
HttpClient is a class for sending HTTP requests to a specific HTTP resource (using its URL) and receiving HTTP responses from the same.
Examples: Making a request to a third-party weather API, ChatGPT etc.
IHttpClientFactory
IHttpClientFactory is an interface that provides a method called CreateClient() that creates a new instance of HttpClient class and also automatically disposes the same instance (closes the connection) immediately after usage.
HttpClient
Properties
BaseAddress
DefaultRequestHeaders
Methods
GetAsync()
PostAsync()
PutAsync()
DeleteAsync()
Subscribe to my newsletter
Read articles from Saurav Kunal directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Saurav Kunal
Saurav Kunal
DevOps Engineer with a strong background in Configuration Management and support roles, skilled in tools like AWS, Docker, Kubernetes, Terraform, and Ansible. I focus on automating processes, improving system performance, and making networks scalable and secure in cloud environments.