Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 1 de octubre de 2013
KatanaEn varias ocasiones hemos comentado la convención que utiliza Katana para localizar el código de inicialización de nuestra aplicación. Por defecto, se espera que dicha inicialización se encuentre en el método Configuration() de una clase llamada Startup que reside en el espacio de nombres principal del proyecto.

En este post veremos cómo modificar esta convención, de forma que podamos implementar la inicialización donde realmente nos apetezca :-)

Pero antes de continuar, permitidme que os recuerde los artículos anteriores de la serie:

1. Consultando al maestro de la Katana

Dicen que no hay mejor forma de aprender que a base cabezazos contra el asunto en cuestión, y eso es justamente lo que vamos a hacer: maltratar el procedimiento de arranque de OWIN para ir descubriendo, en base a sus indicaciones, el camino que debemos seguir.

Y como primera medida, podríamos hacer lo siguiente:
  • Creamos una aplicación web totalmente vacía (Empty Web Application).
  • Instalamos en ella OWIN desde la consola Nuget (recordad siempre añadir el –pre al comando mientras la versión final de Katana no esté disponible):
    PM> Install-Package microsoft.owin.Host.SystemWeb
Ejecutamos el proyecto, y nos damos de frente con una YSOD (Yellow Screen of Death) en la que podemos ver que el host está diciéndonos lo que espera de nosotros para poder localizar la clase de inicialización de la aplicación:
The following errors occurred while attempting to load the app.
- No assembly found containing an OwinStartupAttribute.      
- No assembly found containing a Startup or [AssemblyName].Startup class.
To disable OWIN startup discovery add the appSetting owin:AutomaticAppStartup with a value of "false" in your web.config.
To specify the OWIN startup Assembly, Class, or Method add the appSetting owin:AppStartup with the fully qualified startup class or configuration method name in your web.config.
La primera conclusión a obtener es que la clase de inicialización es obligatoria en toda aplicación OWIN. Y la segunda, que hay varias formas de facilitar al host la localización de dicha clase:
  • Ciñéndonos a una convención preestablecida de nombrado y ubicación de la clase. 
  • Usando un atributo llamado OwinStartupAttribute, con pistas sobre el paradero de la misma.
  • O introduciendo en el web.config un setting llamado “owin:AppStartup” que indique cómo encontrarla.
Iremos viéndolas a lo largo de este artículo.

Por último, también nos indica cómo podemos deshabilitar el proceso de arranque automático estableciendo un setting en el web.config.

Sigamos indagando…

2. Convenciones de nombrado y ubicación

La convención básica y que seguiremos la mayoría de las veces, más que nada por pura comodidad, es que la clase se denomine Startup, tal y como indicaba el mensaje de error anterior. Por tanto, esto es lo que haremos a continuación:
public class Startup
{
    public void Hi()
    {
    }
}
Esta clase podemos crearla fuera de todo espacio de nombres, o bien en el interior del namespace principal de la aplicación, el nombre del ensamblado en las propiedades del proyecto. En cualquiera de estos dos casos, si ejecutamos obtenemos una nueva YSOD en la que se nos indica el siguiente error:
The following errors occurred while attempting to load the app.
- No assembly found containing an OwinStartupAttribute.     
- No 'Configuration' method was found in class 'WebApplication9.Startup, WebApplication9, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
To disable OWIN startup discovery add the appSetting owin:AutomaticAppStartup with a value of "false" in your web.config.
To specify the OWIN startup Assembly, Class, or Method add the appSetting owin:AppStartup with the fully qualified startup class or configuration method name in your web.config.
Bien, algo vamos avanzando, porque el mensaje ha cambiado :-). En esta ocasión, el host ya ha encontrado la clase, pero no ha podido localizar el método Configuration() que esperaba en ella. Vamos a dárselo:
public class Startup
{
    public void Configuration()
    {
    }
}
Ejecutando, obtenemos el tercer YSOD de la tarde:
The following errors occurred while attempting to load the app.
- No assembly found containing an OwinStartupAttribute.
- The 'Configuration' method on class 'WebApplication9.Startup, WebApplication9, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have the expected signature 'void Configuration(IAppBuilder)'.
To disable OWIN startup discovery add the appSetting owin:AutomaticAppStartup with a value of "false" in your web.config.
To specify the OWIN startup Assembly, Class, or Method add the appSetting owin:AppStartup with the fully qualified startup class or configuration method name in your web.config.
Y ahora sí que tenemos todos los datos: en la clase Startup debe existir un método llamado Configuration() de tipo void y que reciba un parámetro de tipo IAppBuilder, con lo que llegamos a la clase que ya hemos estado utilizando en algunos ejemplos:
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // App & pipeline configuration
    }
}
El proceso host, una vez localizada esta clase, la instanciará e invocará al método Configuration() suministrándole el builder que necesita para configurar la aplicación y su pipeline.

Como curiosidad, también se puede optar por utilizar un método estático, e incluso la propia clase podría serlo, obteniendo un resultado idéntico:
public static class Startup
{
    public static void Configuration(IAppBuilder app)
    {
        // App & pipeline configuration
    }
}

3. Modificación de convención usando atributos

A lo largo del punto anterior hemos averiguado dónde espera Katana encontrar la clase y método de configuración mediante la convención que propone por defecto. Pero, por supuesto, se trata de un mecanismo flexible que podemos personalizar utilizando varios medios.

Una de las vías que tenemos es utilizar el atributo de ensamblado [OwinStartup], que ya hemos visto en los mensajes de error aparecidos anteriormente.  Este atributo permite indicar el nombre de la clase que contiene el código de arranque como se puede observar en el siguiente ejemplo:
[assembly: OwinStartup(typeof(MyStartup))]
namespace OwinStartupDemo
{
    public class MyStartup
    {
        public void Configuration(IAppBuilder app)
        {
            // Inititalization code
        }
    }
}
Y de esta forma tan sencilla, dado que aún estamos siguiendo la convención de nombrado para el método, todo funcionaría correctamente. Si quisiéramos, además, modificar el nombre del método, podríamos utilizar un parámetro adicional del atributo:
[assembly: OwinStartup(typeof(MyStartup), "MyConfiguration")]
namespace OwinStartupDemo
{
    public class MyStartup
    {
        public void MyConfiguration(IAppBuilder app)
        {
            // Inititalization code
        }
    }
}
Lo que sí es obligatorio es respetar la signatura del método.

4. Modificación mediante archivo de configuración

Otra fórmula para indicar la clase y método del código de arranque en la aplicación basada en OWIN/Katana es mediante el archivo de configuración (.config) de la aplicación, añadiendo una entrada llamada "owin:appStartup" a la sección <appSettings>. En ella, especificaremos la clase y el espacio de nombres donde se encuentra definida:
<appSettings>
   ...
   <add key="owin:appStartup" value="OwinStartupDemo.MyStartup" />
</appSettings>
Si queremos además indicar el nombre del método, podemos hacerlo separándolo mediante una coma del nombre de la clase:
<appSettings>
   ...
   <add key="owin:appStartup" 
        value="OwinStartupDemo.MyStartup, MyConfiguration" />
</appSettings>
Si en el mismo proyecto se especifica un arranque con OwinStartup y otro desde el archivo de configuración, el primero de ellos tendrá prioridad.

5. Friendly names

Existe un método adicional que combina los dos anteriores para indicar la ubicación del código de inicialización. En líneas generales consiste en:
  • indicar desde el archivo de configuración un “nombre amigable” para identificar el código de inicialización,
  • y usar el atributo OwinStartup para asociar dichos nombres amigables a clases y métodos concretos.
Pero quizás lo veamos mejor con código. La siguiente porción indica en el archivo de configuración que el arranque de la aplicación se encuentra en una ubicación que llamaremos “Staging”:
<appSettings>
   ...
   <add key="owin:appStartup" value="Staging" />
</appSettings>
Ahora, ya en nuestra aplicación, podemos definir un atributo OwinStartup para cada uno de los nombres utilizables a nivel de configuración:
[assembly: OwinStartup("Staging", typeof(StagingStartup))]
[assembly: OwinStartup("Production", typeof(ProductionStartup))]
Básicamente, lo que conseguimos con esta técnica es extraer el nombre de la clase del archivo de configuración y llevárnoslo a código compilable. Así, por ejemplo, si en algún momento refactorizamos el nombre de la clase de configuración, la referencia a la misma desde el atributo será actualizado u obtendremos un error en compilación; si la referencia se encontrara en el web.config en forma de string, tendríamos un error seguro al iniciar la aplicación.

Y aunque también era posible hacer algo parecido sin usar los friendly names, fijaos que el poder modificar desde el archivo de configuración el punto de arranque hace que podamos crear configuraciones para distintos entornos (por ejemplo, de pruebas y de producción) y poder conmutar entre ellos sin necesidad de recompilar, o, usar las transformaciones en despliegue para que el web.cofig de cada entorno apunte a la configuración apropiada.

Por ejemplo, el siguiente código muestra cómo podríamos organizar distintas clases de configuración, que serían activadas mediante los nombres “Staging” y “Production”:
[assembly: OwinStartup("Staging", typeof(StagingStartup))]
[assembly: OwinStartup("Production", typeof(ProductionStartup))]
namespace OwinStartupDemo
{
    public class StagingStartup: ProductionStartup
    {
        public override void Configuration(IAppBuilder app)
        {
            // Include modules for heavy tracing/logging, error pages, etc.
            base.Configuration(app);
        }
    }
    public class ProductionStartup
    {
        public virtual void Configuration(IAppBuilder app)
        {
            // App's initializacion code
        }
    }
}

6. Deshabilitar la detección automática

Para terminar, volviendo al texto de los errores que hemos ido encontrando a lo largo del post, queda un detalle al que todavía no le hemos prestado atención:
To disable OWIN startup discovery add the appSetting owin:AutomaticAppStartup with a value of "false" in your web.config.
Como se puede intuir, el hecho de añadir unas líneas como las siguientes al archivo de configuración hará que no se ejecute el proceso de búsqueda automática:
<appSettings>
   ...
   <add key="owin:AutomaticAppStartup" value="false" />
</appSettings>
Aunque será bastante poco habitual, esto puede ser útil cuando estemos creando sistemas basados en OWIN en los que queramos controlar totalmente el proceso de arranque.

Y con esto hemos acabado con el arranque de aplicaciones OWIN construidas sobre Katana. Pero tranquilos, que todavía nos quedan muchas cosas por ver, y en ello seguiremos durante próximos posts ;-)

Publicado en Variable not found.

5 Comentarios:

Unknown dijo...

Felicidades por la serie de articulos sobre Owin.

Estoy siguiendo este lab, y obtengo todas las YSOD esperadas

En el último paso, cuando todo esta correcto, el navegador no me muestra una YSOD, si no que me da un 404, de not found.

he probado con IIS, y con IISExpress, y lo mismo.

Tienes una idea de cual puede ser el problema, pq es bloqueante y no puedo seguir probando el resto de Labs.

Gracias

josé M. Aguilar dijo...

Hola!

Bueno, es posible que el 404 se deba a que todo ha ido bien, pero no hay ningún middleware en el pipeline que procese la petición?

Un saludo!

Unknown dijo...

pues siguiendo la linea de, continué con el V, el de los midelware, en el que se crea el hello?name=willy, y me da el mismo error de 404.


parece un tema de permisos de directorios, pero estoy con ello y no lo localizo

Unknown dijo...

otro dato,

he instalado con nuget la version 3.0.0-rc2 de Microsoft.Owin.Host.System, y ha instalado las dependencias de Microsoft.Owin 3.0.0-rc2 y Owin 1.0

Otro apunte, es que en el ejemplo de los midelware utilizas, en la expresión lambda, el siguiente fragmento "context.Request.Path.StartsWith", y en la versión que yo tengo Path no dispone del método StartsWith, y lo que utilizo es "context.Request.Path.ToString().StartsWith"

josé M. Aguilar dijo...

Hola, Ángel!

Por lo que dices, suena a que las versiones posteriores a este artículo han modificado detalles de los componentes Owin. Sin embargo, en estos momentos estoy fuera (¡de vacaciones!) y no tengo forma de comprobarlo hay dentro de algunas semanas, lo siento :-(

Muchas gracias y un saludo!