Menú de navegaciónMenú
Categorías

La mejor forma de Aprender Programación online y en español www.campusmvp.es

?id=87a71400-9f42-4859-bec5-3719a2ce271d

Cómo gestionar ajustes (settings) en tus aplicaciones ASP.NET Core en .NET

Imagen ornamental

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 😉

 

José María Aguilar José María atesora una amplísima experiencia trabajando en el mundo del desarrollo de software (programador, analista, responsable de informática, consultor, director técnico), principalmente con tecnologías Microsoft. Actualmente trabaja como consultor y desarrollador independiente, ofreciendo servicios tecnológicos a empresas e instituciones.
Es un reconocido experto en desarrollo web en todo el mundo, y es autor del libro de Microsoft Press "SignalR Programming in Microsoft ASP.NET".
Escribe regularmente artículos sobre ASP.NET MVC y otros temas relacionados con el desarrollo de software en su blog.
Puedes seguirlo en Twitter en @jmaguilar. Ver todos los posts de José María Aguilar
Archivado en: Desarrollo Web

¿Te ha gustado este post?
Pues espera a ver nuestro boletín...

Suscríbete a la newsletter

La mejor formación online para desarrolladores como tú

Agregar comentario

Los datos anteriores se utilizarán exclusivamente para permitirte hacer el comentario y, si lo seleccionas, notificarte de nuevos comentarios en este artículo, pero no se procesarán ni se utilizarán para ningún otro propósito. Lee nuestra política de privacidad.