Tradicionalmente, los desarrolladores que hemos trabajado con ASP.NET "clásico" guardábamos los settings o valores de configuración de nuestras aplicaciones en el célebre archivo Web.config
. En él era frecuente encontrar cadenas de conexión, valores de constantes, switches para configurar comportamientos de la aplicación, etc.
En ASP.NET Core este sistema se rediseñó por completo y el resultado es una infraestructura de configuración mucho más potente y flexible.
Ahora, los parámetros de configuración pueden obtenerse desde prácticamente cualquier origen: archivos JSON, XML, los tradicionales archivos .ini
, variables de entorno del sistema operativo y otras fuentes de datos, pues se trata de un componente extensible. Incluso es posible, y de hecho es lo más habitual, utilizar más de una fuente de configuración por proyecto.
Siguiendo la estructura modular de ASP.NET Core, tanto el propio sistema de configuración como los distintos tipos de origen se distribuyen como paquetes independientes; a continuación se muestran unos ejemplos:
Paquete |
Componente |
Microsoft.Extensions.Configuration |
Núcleo del sistema de configuración |
Microsoft.Extensions.Configuration.Json |
Soporte para archivos JSON |
Microsoft.Extensions.Configuration.Ini |
Soporte para archivos INI |
Microsoft.Extensions.Configuration.Xml |
Soporte para archivos XML |
Microsoft.Extensions.Configuration.EnvironmentVariables |
Soporte para variables de entorno del sistema operativo |
Microsoft.Extensions.Configuration.CommandLine |
Soporte para parámetros de la línea de comandos |
Microsoft.Extensions.Configuration.KeyPerFile |
Soporte para settings por archivo |
Microsoft.Extensions.Configuration.AzureKeyVault |
Soporte para Azure Key Vault |
Microsoft.Extensions.Configuration.UserSecrets |
Soporte para User secrets |
Nota: Microsoft.AspNetCore.App
ya contiene las referencias a casi todos ellos, por lo que en la mayoría de las ocasiones no tendrás que hacer nada para traer a tu aplicación estas funcionalidades.
Configuración por defecto de aplicaciones ASP.NET Core
Como sabrás, durante el arranque de las aplicaciones ASP.NET Core, en el archivo Program.cs
, se utiliza un IWebApplicationBuilder
para configurar distintos aspectos del funcionamiento de las mismas. Uno de estos aspectos es el sistema de settings.
La llamada a WebApplication.CreateBuilder()
que se incluye en todas las plantillas de proyecto se encarga de configurar las aplicaciones para que carguen los settings desde los siguientes orígenes:
- El archivo
appsettings.json
- El archivo
appsettings.{entorno}.json
, siendo {entorno}
el entorno de ejecución actual (development, production, etc.)
- Sólo en el entorno "Development", secretos de usuario
- Variables de entorno del sistema operativo
- Parámetros de línea de comandos
IMPORTANTE: el orden en el que se establecen los distintos orígenes es importante. Si el mismo setting está definido en varios de ellos, el valor devuelto al obtenerlo será el de la fuente añadida en último lugar. Por ejemplo, en el caso anterior, si existe un setting llamado connectionstring
en el archivo appsettings.json
y de nuevo se establece como una variable de entorno del sistema operativo, el valor que llegará a la aplicación será este último.
Un ejemplo de contenido del archivo JSON appsettings.json
podría ser el siguiente:
{
"title": "My application",
"options": {
"stringOption": "Hello",
"boolOption": true,
"integerOption": 42
}
}
En la configuración por defecto este archivo es opcional, y si se modifica su contenido en tiempo de ejecución el entorno será consciente, de forma que las aplicaciones podrán acceder a los valores actualizados.
Nota: esto difiere de ASP.NET "clásico", donde cualquier cambio en los parámetros de configuración del archivo web.config
hacía que la aplicación se reiniciase para recargar la nueva configuración.
ASP.NET Core también intentará cargar los settings desde un archivo, también opcional, llamado appsettings.{env}.json
, donde {env}
será sustituido por el nombre del entorno de ejecución activo (appsettings.development.json
, appsettings.production.json
, etc.) La estructura de este archivo no tiene por qué coincidir con el anterior, simplemente los settings serán fusionados (o sobrescritos si ya existían), por ejemplo:
{
"title": "My application (development)",
"options": {
"decimalOption": 18.42
}
}
Nota: observa que esta fórmula permite conseguir algo parecido a lo que teníamos usando transformaciones del archivo web.config
en ASP.NET "clásico". En el archivo appsettings.json
tendremos los settings por defecto, y luego sobrescribiremos en los distintos appsettings.{environment}.json
las opciones específicas para cada entorno de ejecución.
Tras estos dos archivos, ASP.NET Core continuará obteniendo settings desde el resto de orígenes configurados, añadiendo nuevas opciones o modificando los valores existentes. Así, si en el momento de ejecución existe una variable de entorno de sistema operativo llamada "title"
, el valor de esta será el utilizado desde la aplicación. Y lo mismo ocurrirá si se establece desde línea de comandos, así:
c:\MyApp>dotnet run title="New title"
Acceso a settings desde la aplicación
Para acceder a los settings de configuración de la aplicación, debemos utilizar una instancia de IConfiguration
, que provee de las herramientas necesarias para obtener los valores.
En el caso de estar implementando middlewares o handlers directamente en Program.cs
, lo tendremos bastante sencillo, pues tanto el objeto builder
como app
exponen una propiedad Configuration
con una instancia de IConfiguration
ya materializada. Por tanto, podremos usarla directamente para acceder a los settings, como en el siguiente ejemplo:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(async (ctx) =>
{
await ctx.Response.WriteAsync($"Title: {app.Configuration["title"]}");
});
app.Run();
Esta forma tan sencilla es válida cuando estamos en Program.cs
porque tenemos a mano instancias de IConfiguration
, pero en general tendremos que hacer uso del sistema de inyección de dependencias para obtenerlas cuando queramos leer configuraciones desde otros puntos de la aplicación. Por ejemplo, la siguiente clase solicita un objeto IConfiguration
en su constructor:
public class EmailSender: ISender
{
private readonly IConfiguration _configuration;
public EMailSender(IConfiguration configuration)
{
_configuration = configuration;
}
public Task SendAsync(string text, string target)
{
var smtpServer = _configuration["SmtpServer"];
var smtpCredentials = _configuration["SmtpCredentials"];
var sender = _configuration["Sender"];
... // Enviar email
}
}
En cualquier caso, como se puede observar, es posible acceder a las propiedades utilizando la instancia de IConfiguration
como si se tratase de un diccionario clave-valor. En el caso de propiedades complejas, podemos utilizar los dos puntos ":" como separador para navegar por la estructura de la configuración:
var stringOption = app.Configuration["options:stringOption"]; // "Hello"
Acceso tipado a settings
Aunque la forma de acceder a los settings que hemos visto puede ser suficiente en escenarios simples, está claro que el hecho de utilizar como índice una simple cadena de texto (configuration["title"]
), puede ser una fuente de problemas. Obviamente estas cadenas no son sometidas a ningún tipo de control en compilación y cualquier cambio de estructura o nombre de setting en el archivo de configuración podría provocar comportamientos incorrectos en nuestra aplicación.
Por ejemplo, continuando con el ejemplo anterior, si en algún momento decidiéramos cambiar el setting "title"
por "applicationTitle"
en los archivos de configuración, nos veríamos obligados a revisar toda la aplicación en busca de accesos a esta propiedad con objeto de actualizar las referencias a la nueva denominación.
Existe una fórmula muy sencilla para conseguir acceso tipado a las configuraciones de la aplicación utilizando el sistema integrado de inyección de dependencias de ASP.NET Core. Para ello, lo primero que tenemos que hacer es crear una clase o grafo cuyas propiedades coincidan con los settings de la aplicación, como las que vemos a continuación, que son apropiadas para los archivos de configuración de ejemplo que vimos más arriba:
public class MyAppSettings
{
public string Title { get; set; }
public AppOptions Options { get; set; }
}
public class AppOptions
{
public string StringOption { get; set; }
public bool BoolOption { get; set; }
public int IntegerOption { get; set; }
}
A continuación, debemos registrar en el contenedor de dependencias los servicios necesarios para que este mecanismo funcione, y asociar la clase MyAppSettings
con la instancia de IConfiguration
que tenemos disponible en el builder:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOptions();
builder.Services.Configure<MyAppSettings>(builder.Configuration);
...
Con esto, ASP.NET Core pondrá a nuestra disposición una instancia de IOptionsSnapshot<MyAppSettings>
en el inyector de dependencias, que podremos utilizar durante el proceso de las peticiones para acceder al objeto MyAppSettings
que contiene la configuración:
app.Run(async ctx =>
{
var options = ctx.RequestServices.GetService<IOptionsSnapshot<MyAppSettings>>();
MyAppSettings settings = options.Value;
var title = settings.Title;
var stringOption = settings.Options.StringOption;
await ctx.Response.WriteAsync($"Title: {title}, Options.StringOption: {stringOption}");
});
Nota: aunque ahora se ha hecho así por concreción, la forma de solicitar la instancia al contenedor de dependencias usando GetService<T>
no es muy recomendable. Siempre que sea posible, procuraremos obtener la instancia usando inyección de dependencias en el constructor de las clases.
La instancia de MyAppConfiguration
que obtenemos a través de IOptionsSnapshot<MyAppSettings>
siempre está "fresca", es decir, sus propiedades son actualizadas automáticamente cuando se modifican los archivos de origen. Si no deseásemos obtener valores actualizados, podemos usar en su lugar IOptions<MyAppSettings>
.
Conclusión
Aunque en este artículo solo hemos arañado la superficie, puedes ver que manejar ajustes de tu aplicación Web en .NET es un proceso sencillo y extremadamente flexible, que te valdrá para cualquier tipo de aplicación basada en ASP.NET Core, tanto MVC, como servicios, aplicaciones Blazor...
Existen otras muchas cuestiones relacionadas con la gestión de ajustes: escribir y persistir los cambios, extender el sistema de ajustes con fuentes propias, almacenar secretos... Puedes leer más sobre el sistema de configuración en la documentación oficial de ASP.NET Core.
Y por si acaso quieres dominar en serio y a fondo tanto ASP.NET Core como MVC, ya estás tardando 😉
Fecha de publicación: