Cómo usar el custom authentication de Azure Mobile Services .NET back-end

Trabajando con ZUMO .NET back-end tuve la necesidad de implementar una autenticación personalizada, es decir, que no sea con proveedores de terceros sino con usuarios y contraseñas que están en x base de datos. Afortunadamente para mi, Microsoft tiene esta documentación, así que no me esforzaré en reescribir lo que allí se explica. Lo que allí no comentan es el lío que hay con las versiones de los paquetes, cómo usar el token desde un cliente ni cómo obtenerla identidad del usuario en el lado del servidor.

TL;DR;

No actualicen la versión del System.IdentityModel.Tokens.Jwt Usar el header “X-ZUMO-AUTH” para pasar el token al servicio.

Líos con las versiones de los paquetes

Una vez creado el proyecto lo primero que hice fue actualizar los paquetes, hace falta agregar unos cuantos assembly binding redirects y cuando todos ya están listos falla con pantalla amarilla y  el error:

Method not found: 'Void System.IdentityModel.Tokens.JwtSecurityTokenHandler.set_CertificateValidator(System.IdentityModel.Selectors.X509CertificateValidator)'. 

Si ya la has cagado como yo, la solución esta en ejecutar dos comandos en el Package Manager Console:

> uninstall-package System.IdentityModel.Tokens.Jwt -Version 3.0.2 -Force
> uninstall-package System.IdentityModel.Tokens.Jwt -Version 4.0.1 -Force
> install-package System.IdentityModel.Tokens.Jwt -Version 3.0.2

En los comentarios de la entrada en inglés dicen que aparentemente la autenticación no se realiza por culpa de la versión de este paquete, pero en lo que probé parece que solo es lío de usar la cabecera correcta.

Cabecera de autenticación

Después de leer algo de la documentación no encontré nada que dijera algo sobre la cabecera X-ZUMO-AUTH y al hacer la búsqueda parece que no hay documentación oficial (Si encuentran algo no duden es comentarlo). La encontré después de leer algunos blogs. En fin, para poder usar el token de autenticación  hay que pasar la cabecera X-ZUMO-AUTH y no el Authorization. El token lo recuperamos haciendo un request a /customlogin/ y este será pasado a las acciones que requieran autenticación.   image   image   image

Obtener el usuario en el back-end

Aunque WAMS es una envoltura al rededor de ASP.NET WEB API y otros servicios, ciertamente hay cosas que cambian y aún no entiendo muy bien, cómo por qué cambia el Type del claim que guarda la identidad del usuario. Basta con hacer algo de debug para encontrarlo y recuperarlo de la forma:

    [AuthorizeLevel(AuthorizationLevel.User)]
    public class HelloController : ApiController
    {
        public IHttpActionResult Get()
        {
            var user = ClaimsPrincipal.Current.FindFirst(claim => claim.Type == "uid").Value;
            if (user.Contains(":")) user = user.Split(':')[1];
            return Ok("Hello! " + user);
        }
    }

Conectando con JavaScript

El proyecto en el que estoy trabajando en este momento requiere que desde un sitio web se invoquen ciertas acciones expuestas en el servicio móvil. Lo que nos pone ahora la tarea de solucionar el lío del cors. Por fortuna para nosotros desde la actualización 1.0.342 se usa  ASP.NET WEB API 2, y entre sus características está el trabajo con CORS, pero por supuesto, encapsuladas. Para usarla solo basta con agregar un nuevo appSetting en el web.config:

 
<add key="MS_CrossDomainOrigins" value="http://mysite.com" />

Con esto ya podemos llamar desde el cliente js:

 
        $.ajax({
            type: 'POST',
            contentType : "application/json",
            url: 'http://localhost:54085/api/customlogin',
            data: JSON.stringify(userModel)
        }).done(function (data) {
            sessionStorage.setItem('supertoken', data.authenticationToken);
            AppViewModels.assignVM.initView();
        }).fail(function() {
            alert("Usuario o contraseña invalidos");
        });

El código del ejemplo esta aquí

Espero les sea de utilidad.

Hasta el próximo post.

Cómo usar el custom authentication de Azure Mobile Services .NET back-end

[AutoMapper] Convertir de Utc a determinada zona horaria

Dado un modelo como el siguiente:

public class User
{
	//Otras propiedades...
	public string TimeZoneId { get; set; }		
}

public class UserAction
{
	//Otras propiedades...
	public DateTime DateInUtc { get; set; }
}

Al registrar una acción del usuario registraremos la fecha en utc, pero a la hora de recuperarla y mostrarla al usuario, esta deberá ser de nuevo convertida, de Utc a la zona horaria del usuario. Si recuperamos una colección de acciones del usuario sería necesario crear una colección del ViewModel y en un bucle construir las fechas:

var timeZoneId = GetTimeZoneId();
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
var userActions = GetUserActions();
var viewModel = new List<UserActionViewModel>();
foreach(var userAction in userActions)
{
	viewModel.Add(new UserActionViewModel()
		{
			ValidDate = TimeZoneInfo.ConvertTimeFromUtc(userAction.DateInUtc, timeZoneInfo)
		});
}

Podemos extraer el código y reutilizarlo para construir los ViewModels que necesitemos y hagan uso de las fechas y las zonas horarias. Pero si ya hemos incluido AutoMapper como una dependencia más de nuestro proyecto… ¿cómo lo podemos hacer?

IMappingOperationOptions y ValueResolver

AutoMapper ofrece la opción de “pasar información” al custom map haciendo uso de la segunda sobrecarga del método Map. Algo de la forma:

var timeZoneId = GetTimeZoneId();
var viewModel = Mapper.Map<UserActionViewModel>(userAction, opts=>opts.Items.Add("timezone", timeZoneId));

Y ahora ¿Cómo recuperamos dicho valor en el custom map?. Debemos convertir de alguna forma el valor de origen y recuperar el TimeZoneId. Para esto haremos uso de un ValueResolver, pues nos ofrece la posibilidad de acceder al contexto. Algo de la forma:

        class LocalizedDateTimeResolver : IValueResolver
        {
            public ResolutionResult Resolve(ResolutionResult source)
            {
                var timeZoneId = source.Context.Options.Items["timezone"].ToString();
                var utc = (DateTime)source.Value;
                var timezoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
                var dateTime = TimeZoneInfo.ConvertTimeFromUtc(utc, timezoneInfo);
                return source.New(dateTime);
            }
        }

Con esto, en el custom map podemos usar el método ResolveUsing, de la forma:

            Mapper.CreateMap<UserAction, UserActionViewModel>()
                .ForMember(des => des.ValidDate,
                    opts => opts.ResolveUsing<LocalizedDateTimeResolver>().FromMember(src => src.DateInUtc));

Espero les sea de utilidad. Hasta el próximo  post.

[AutoMapper] Convertir de Utc a determinada zona horaria

[Entity Framework] Encapsular las Fk de las entidades

La ultima vez que use Entity Framework quedé con un disgusto por como terminé modelando los objetos, en parte por ignorancia y en parte por imposiciones y convenciones propias del ORM.

En esta ocasión, me encontré con un caso de propiedades de navegación y las propiedades “{*}Id” que usa Entity Framework para construir las relaciones. Esto me sirvió para recordar como funcionan estas convenciones y ver si puedo crear un modelo menos anémico y más ajustado a la realidad.

Dado el siguiente modelo:

    public class Entity
    {
        protected Entity()
        {
            Id = Guid.NewGuid();
        }

        public Entity(Guid id)
        {
            Id = id;
        }

        public Guid Id { get; private set; }
    }

    public class User : Entity
    {
        //EF
        protected User() { }

        public User(string name, string lastName)
        {
            LastName = lastName;
            Name = name;
        }

        public void FinishRegister(Country country)
        {
            Country = country;
        }

        public string Name { get; private set; }

        public string LastName { get; private set; }

        public Country Country { get; private set; }
    }

    public class Country : Entity
    {
        //EF
        protected Country() { }

        public Country(string name, string iso)
        {
            Iso = iso;
            Name = name;
        }

        public string Name { get; private set; }

        public string Iso { get; private set; }
    }

Vemos en el modelo ya una convención de EF, el uso de la propiedad virtual para poder generar los proxies que serán usados en el lazy loading y change tracking… pero continuemos.

La propiedad virtual es una convención en EF que creará una columna FK, uniqueidentifier, allow nulls con el nombre de la propiedad y el sufijo “_Id”. Lo que se ajusta perfecto a nuestro modelo.

Supongamos ahora que el usuario se registra con su nombre y apellido, y luego finalizará su proceso de registro. En este proceso solicitamos toda la información restante. Por practicidad del ejemplo solo será el país.

Este modelo se ajusta bien a los requerimientos pero si lo llevamos a la practica no será muy “optimo“, es decir, suponiendo un entorno web: en una petición POST, para finalizar el registro, en los datos de la petición solo enviaremos el Id del país y será necesario entonces consultar la base de datos para recuperar el el usuario y el país (por su Id) y luego si finalizar el registro. Desde mi punto de vista la consulta que sobraría es la del país.

            using (var context = new ApplicationDbContext())
            {
                var user = context.Users.Find(viewModel.UserId);
                var country = context.Countries.Find(viewModel.SelectedCountry);
                user.FinishRegister(country);
                context.SaveChanges();
            }

Entonces la solución pasaría por contaminar el modelo usando una propiedad Id que represente la identidad del país y aprovechar las bondades del orm, que para eso lo estamos usando! El modelo queda de la forma:

    public class User : Entity
    {
        //EF
        protected User() { }

        public User(string name, string lastName)
        {
            LastName = lastName;
            Name = name;
        }

        public void FinishRegister(Guid countryId)
        {
            CountryId = countryId;
        }

        public string Name { get; private set; }

        public string LastName { get; private set; }

        public Guid? CountryId { get; private set; }

        public Country Country { get; private set; }
    }

En este caso usamos la convención de llaves foraneas de EF y ya podremos usar solo el Id del país para finalizar el proceso de registro.

Lo que no me gusta de esta solución es tener que exponer al publico la propiedad CountryId  pues esto no representa nada en el exterior, para eso está la propiedad del tipo País que ya definimos. Entonces lo mejor sería ocultarla del exterior quitándole su modificador de acceso publico. Pero al quitarle el acceso publico EF no entenderá la convención de llaves foráneas y  el código que finaliza el registro no servirá, porque no entiende que ese GUID es la clave de país.

Ahora la pregunta es: ¿Cómo mapear propiedades no publicas en Entity Framework?… y Stack Overflow sale al rescate.

En síntesis agregamos otro contaminante al modelo, el atributo Column, y generaremos una convención que se encargue de hacer el mapping de estas propiedades (no publicas y marcadas con dicho atributo) vía reflection. El código del contexto está en este gist

De esta forma ya podemos Finalizar el registro haciendo uso del Id pero sin exponer propiedades extra al exterior.

Espero les sea de utilidad.

Hasta el próximo post.

[Entity Framework] Encapsular las Fk de las entidades

[How To] NodeJS, Sinatra, NGINX y Azure Worker Role

UPDATE: Ya esta en este repositorio el código de este ejemplo. El orden ha cambiado y el código de los scripts es más fácil de entender ahora.

En el post anterior vimos como correr NGINX dentro de un Azure Node Worker Role, aunque de NodeJS no vimos nada, pues la configuración la dejamos pendiente para una siguiente entrada. Así pues, veamos como correr NodeJS dentro de nginx en nuestro Azure Worker Role.

1- Crear archivo de configuración para nginx

1.1- Nginx usa un archivo de configuración localizado en la ruta /nginx/conf/nginx.conf Este archivo trae una configuración de forma predeterminada, configuración que debemos sobrescribir para poder usar el proxy inverso y llamar, por ejemplo, a nuestro nodejs.

La mejor forma de hacerlo, para no complicarnos mucho, es crear una copia del nginx.conf en la raíz del WorkerRole1 y agregar la siguiente entrada:

image

1.2- Ahora necesitamos que al finalizar la descarga y el unzip del nginx sea reemplazada la configuración predeterminada por nuestro nuevo .conf. Para esto agregamos esta línea Copy-Item "$current\nginx.conf" "$current\nginx-1.6.0\conf\" -force a el archivo download_nginx.ps1 (archivo que vimos en el articulo anterior). El script queda así:

$current = [string] (Get-Location -PSProvider FileSystem)
$webclient = New-Object System.Net.WebClient
$url = "http://nginx.org/download/nginx-1.6.0.zip"
$file = "$current\nginx.zip"
$webclient.DownloadFile($url,$file)

$shell_app=new-object -com shell.application
$zip_file = $shell_app.namespace($file)
$destination = $shell_app.namespace($current)
$destination.Copyhere($zip_file.items())
Remove-Item nginx.zip

Copy-Item "$current\nginx.conf" "$current\nginx-1.6.0\conf\" -force

$firewallFile = "$current\nginx-1.6.0\nginx.exe"
netsh advfirewall firewall add rule name="nginx" dir=in action=allow program="$firewallFile" enable=yes
cd "nginx-1.6.0"
& ".\nginx.exe"

1.3- Si ejecutamos el Emulador de Azure y navegamos al http://localhost/ veremos algo como:

image

Y si apuntamos a /nodeapp veremos algo como:

image

Y en las herramientas de captura web encontraremos:

image

Hasta aquí este post sería muy corto, así que mostraré como correr Sinatra, a la par, dentro del NGINX.

2- Descargar Ruby e instalar Sinatra

2.1- Agregaremos un archivo llamado download_ruby.ps1 a la raíz del WorkerRole1. El código de este archivo será el siguiente:

$current = [string] (Get-Location -PSProvider FileSystem)
$webclient = New-Object System.Net.WebClient
$url = "http://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-2.0.0-p481.exe"
$file = "$current\ruby.exe"
$webclient.DownloadFile($url,$file)

Invoke-Expression "$file /silent"
Remove-Item ruby.exe
& "$current\install_sinatra.cmd"

El código anterior descargará la versión 2 de Ruby que encontramos en Ruby Installer. Ejecutará el instalador de forma silenciosa, removerá el .exe y finalmente llamará a otro .cmd que será el encargado de instalar Sinatra.

2.2- Agregamos un archivo llamado install_sinatra.cmd en la raíz del WorkerRole1. con el siguiente código:

 
D:\Ruby200\bin\gem.bat install sinatra --no-ri --no-rdoc

NOTA: En mi maquina de desarrollo (como en muchas) los programas se instalan de forma predeterminada en la partición C del disco duro. En las maquinas virtuales de Azure lo hacen en D:\. No olvides cambiar el cmd o agregar alguna validación para que funcione en ambos entornos.

2.3- Agregaremos ahora un archivo main.rb a la raíz del WorkerRole1, este archivo tendrá alguna configuración de Sinatra y será el encargado de responder las solicitudes que hagamos a la ruta “/”. El código de este archivo es:

require 'sinatra'
set :environment, :development
set :port, 1338
get '/' do
  "Hello, my name is Sinatra"
end

2.4- Ahora que tenemos la aplicación de Ruby lista necesitamos iniciarla, para esto agregaremos u nuevo archivo llamado start_ruby.cmd con una sola línea de código:

D:\Ruby200\bin\ruby.exe main.rb

2.5- Con esto solo nos queda invocar los .cmd correspondientes desde el Worker Role. Para esto agregaremos dos Task al Service Definition, una llamando al .cmd encargado de descargar e instalar Ruby y otra para iniciar nuestra app:

      <Task commandLine="setup_ruby.cmd" executionContext="elevated" taskType="background" />
      <Task commandLine="start_ruby.cmd" executionContext="elevated" taskType="background" />

Las entradas en el Service Definition quedan así :

image

3- Editar el nginx.conf

3.1- Para decirle a nginx que corra Sinatra editaremos la entrada location / del nginx.conf que agregamos, queda así:

        location / {            
            proxy_pass   http://127.0.0.1:1338;
            #root   html;
            #index  index.html index.htm;
        }   

3.2- Si ejecutamos ahora el emulador y navegamos a al localhost veremos algo como:

sinatranodeng

Known Issues

1- Se hace necesario agregar enviaroment variables a las tasks que ejecutan la instalación de Ruby y Sinatra. Esto para poder alternar, por ejemplo, donde se busca el ruby.exe en los .cmd.

2- He notado que en ocasiones(a veces si, a veces no) no se ejecutan todos los Scripts al desplegar en la nube. Aún desconozco la razón de este comportamiento. Por ello se hace necesario habilitar la conexión por escritorio remoto al Azure Role para poder terminar la instalación.

3- Si se cambia la entrada del location de Sinatra en el nginx.conf. Sinatra responde con un 404.

Notas finales

1- Cabe anotar que nada de lo mostrado aquí ha sido probado en entornos de producción.

2- Cualquier sugerencia por favor no dude en comentarlo.

3- En los próximos días publicaré el repo en mi GitHub. Revisar el update

Espero les sea de utilidad.

Hasta el próximo post.

[How To] NodeJS, Sinatra, NGINX y Azure Worker Role

[How to] Correr NGINX en un NodeJS Azure Worker Role

No hace mucho empecé a jugar con nginx para correr django y nodejs dentro del mismo server, haciendo uso de su propiedad de proxy inverso. Ésta es solo una de sus características, pero hablar de nginx daría para muchos artículos, y además, hay documentación oficial del producto.

Estas pruebas las venía realizando dentro de mi maquina de desarrollo, nunca he probado o trabajado con nginx en un entorno productivo, así que, para tratar de llevar un poco más allá estas pruebas me decidí a desplegar el nginx dentro de un worker role que me respondiera las solicitudes al nodejs y demás.

Vale aclarar que existen más formas de hacerlo, como haciendo deploy de una imagen de maquina virtual que ya tenga el nginx o creando una maquina virtual de Ubuntu e instalando y configurando el servidor.

1. Crear el NodeJS Azure Cloud Service

Sobre este tema ya se escribió un tutorial completo en el sitio de azure, pero los pongo aquí para que se entienda.

1.1-  Abrimos el Azure Power Shell con privilegios de administrador, ubicamos el directorio en que vamos a trabajar y ejecutamos el comando New-AzureServiceProject testnginx

image

1.2- Ahora, dentro de la carpeta que creó el comando anterior agregamos un Azure Worker Role con el comando Add-azurenodeworkerrole

image

1.3- Ya con esto podemos probar que está funcionando, haciendo uso del comando start-azureemulator y navegando en el browser a la dirección indicada.

2. Descargar e iniciar nginx

2.1- Lo primero que haremos será revisar que es lo que han hecho estos comandos que ejecutamos en el paso uno. Para ello usaré Sublime Text (aunque no hace falta usar editores complejos, con el File explorer y notepad es suficiente)

image

De entrada vemos la carpeta WorkerRole1, ésta contiene un par de .cmd, .js y un .ps1. Para ver como interactúan estos archivos solo hace falta revisar el ServiceDefinition.csdef. En este ubicaremos el Startup y cada una de sus Tasks. Estas tareas llaman a cada uno de los .cmd que ya habíamos visto antes. Y estos .cmd ejecutarán las tareas que tengan programadas, como por ejemplo, descargar nodejs.exe. Viendo esto podemos darnos cuenta por donde es el camino y empezaremos a realizar todas las tareas de scripting que necesitemos.

2.2- Agregaremos una Task al Startup del Role, esta tarea llamará a un archivo setup_nginx.cmd:

image

De hecho, esta será la única modificación que haremos sobre el ServiceDefinition. Queda así:

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="testnginx" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WorkerRole name="WorkerRole1">
    <Startup>
      <Task commandLine="setup_worker.cmd &gt; log.txt" executionContext="elevated">
        <Environment>
          <Variable name="EMULATED">
            <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
          </Variable>
          <Variable name="RUNTIMEID" value="node" />
          <Variable name="RUNTIMEURL" value="" />
        </Environment>
      </Task>
      <Task commandLine="node.cmd .\startup.js" executionContext="elevated" />
      <Task commandLine="setup_nginx.cmd" executionContext="elevated" taskType="background" />
    </Startup>
    <Endpoints>
      <InputEndpoint name="HttpIn" protocol="tcp" port="80" localPort="80"/>
    </Endpoints>
    <Runtime>
      <Environment>
        <Variable name="PORT">
          <RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/Endpoints/Endpoint[@name='HttpIn']/@port" />
        </Variable>
        <Variable name="EMULATED">
          <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
        </Variable>
      </Environment>
      <EntryPoint>
        <ProgramEntryPoint commandLine="node.cmd .\server.js" setReadyOnProcessStart="true" />
      </EntryPoint>
    </Runtime>
  </WorkerRole>
</ServiceDefinition>

2.3- En la carpeta WorkerRole1 agregaremos el archivo setup_nginx.cmd con el siguiente código:

@echo off
powershell -c "Set-ExecutionPolicy Unrestricted"
powershell .\download_nginx.ps1

Este código lo único que haremos es llamar al script downloadnginx.ps1

2.4- En la carpeta WorkerRole1 agregamos un archivo llamado download_nginx.ps1 con el siguiente código:

$current = [string] (Get-Location -PSProvider FileSystem)
$webclient = New-Object System.Net.WebClient
$url = "http://nginx.org/download/nginx-1.6.0.zip"
$file = "$current\nginx.zip"
$webclient.DownloadFile($url,$file)

$shell_app=new-object -com shell.application
$zip_file = $shell_app.namespace($file)
$destination = $shell_app.namespace($current)
$destination.Copyhere($zip_file.items())
Remove-Item nginx.zip

$firewallFile = "$current\nginx-1.6.0\nginx.exe"
netsh advfirewall firewall add rule name="nginx" dir=in action=allow program="$firewallFile" enable=yes
cd "nginx-1.6.0"
& ".\nginx.exe"

Este código se divide en tres secciones. La primera crea un webclient apuntando a la dirección de descarga de la ultima versión estable de nginx, la descarga y la deja en la carpeta “current”. La segunda sección hace un unzip de los archivos y borra el .zip. La tercera y ultima sección del script agrega una regla en el firewall y ejecuta el nginx.exe

2.5- Por ahora (aún no vamos a correr nodejs dentro del nginx) cambiaremos el puerto del nodejs al 1337. Esto en el archivo server.js:

image

2.6- Si ejecutamos el comando start-azureemulator y navegamos al localhost veremos algo como:

image

3- Desplegar en la nube

Sobre esto también se ha escrito documentación en el sitio oficial pero la transcribiré aquí para no perder el hilo de la historia.

3.1- Lo primero será agregar el Azure Account con el comando Add-Azureaccount

3.2- Descargamos el publication settings con el comando Get-AzurePublishSettingsFile

3.3- Importamos el archivo que habíamos descargado con el comando Import-AzurePublishSettingsFile "path donde esta el archivo"

3.4- publicamos con el comando Publish-AzureServiceProject -ServiceName nombre_del_servicio_en_la_nube -Location "East US" -Launch

Espero les sea de utilidad.

Hasta el próximo post.

[How to] Correr NGINX en un NodeJS Azure Worker Role

[NancyFx] Vistas y contenido estático en diferente ensamblado

Gracias a OWIN (los componentes compatibles) podemos mover nuestra aplicación entre diferentes hosts y servidores sin mucho esfuerzo, por ésta razón, en ocasiones separamos físicamente la aplicación del host (en diferentes ensamblados).

Nancy tiene unas configuraciones predefinidas para localizar las vistas dentro del entrypoint, configuraciones que podemos sobrescribir para trabajar en la forma que deseemos, en este caso, localizar las vistas en un ensamblado distinto:

Lo primero que debemos hacer es cambiar las propiedades Build Action y Copy to Output Directory a Embedded Resource  y Do Not Copy respectivamente:

image 

Ahora necesitamos un Bootstrapper (o editar el que ya teníamos) para que Nancy pueda localizar las vistas dentro de nuestro ensamblado. Haremos uso del ResourceViewLocationProvider y su propiedad RootNamespaces para agregar el espacio de nombres que queremos. Finalmente sobrescribimos la InternalConfiguration:

    public class CustomNancyBootstraper : DefaultNancyBootstrapper
    {
        protected override void ConfigureApplicationContainer(TinyIoCContainer container)
        {
            var assembly = GetType().Assembly;
            ResourceViewLocationProvider.RootNamespaces.Clear();
            ResourceViewLocationProvider.RootNamespaces.Add(assembly, "Ensamblado.PathVistas"); // Donde se estan tus views
            base.ConfigureApplicationContainer(container);
        }
        protected override NancyInternalConfiguration InternalConfiguration
        {
            get
            {
                return NancyInternalConfiguration.WithOverrides(config => { config.ViewLocationProvider = typeof(ResourceViewLocationProvider); });
            }
        }
    }

¡Y listo! con estos pasos ya podemos tener las vistas en un ensamblado distinto.

¿Y el contenido estático?

El ResourceViewLocationProvider no nos sirve para el contenido estático dentro del sitio, así que debemos agregar las convenciones necesarias para localizar estos recursos (por ahora a pedal):

        protected override void ConfigureConventions(NancyConventions nancyConventions)
        {
            nancyConventions.StaticContentsConventions.Clear();            
            nancyConventions.StaticContentsConventions.Add(Css);
            base.ConfigureConventions(nancyConventions);
        }

        private Response Css(NancyContext context, string s)
        {
            const string resourcePath = @"/Content";
            var path = context.Request.Url.Path;
            if (!path.StartsWith(resourcePath, StringComparison.InvariantCultureIgnoreCase))
            {
                return null;
            }
            return new EmbeddedFileResponse(GetType().Assembly, "Ensamblado.PathContent", Path.GetFileName(context.Request.Url.Path));
        }

¡No sale mi Favicon!

Como vemos los contenidos deberán ser recursos embebidos y el favicon no es la excepción. Para que Nancy lo encuentre debemos sobrescribir la propiedad FavIcon, como dice la documentación:

        private byte[] _favicon;
        protected override byte[] FavIcon
        {
            get { return _favicon ?? (this._favicon = LoadFavIcon()); }
        }
        private byte[] LoadFavIcon()
        {
            using (var resourceStream = GetType().Assembly.GetManifestResourceStream("Ensamblado.favicon.ico"))
            {
                var tempFavicon = new byte[resourceStream.Length];
                resourceStream.Read(tempFavicon, 0, (int)resourceStream.Length);
                return tempFavicon;
            }
        }

El código completo de éste articulo se encuentra en este Gist.

Espero les sea de utilidad.

Hasta el próximo post.

[NancyFx] Vistas y contenido estático en diferente ensamblado

Azure Service Bus Queue Client + Reactive Extensions

Azure Service Bus soporta dos modelos de mensajería asíncrona basados en colas, éstos son: las colas y los tópicos. Las colas ofrecen una comunicación 1:1, es decir, se publica un mensaje en la cola y de los n receptores sólo uno podrá trabajar el mensaje. Por el contrario, los tópicos, responden de una manera más fiel a un modelo pub/sub, y nos permiten una comunicación 1:n, es decir, el mensaje que se publica en un tópico puede ser recibido por uno o varios  subscritores que podrán o no hacer uso de filtros para recibir solo los mensajes que les interesan; esto se logra haciendo uso de un sistema de colas virtuales para cada subscripción, sistema que controla Azure por nosotros.

Cada modelo responde a necesidades distintas y no son excluyentes, es decir, en un mismo namespace podemos estar trabajando con colas y tópicos al mismo tiempo y usarlas para responder a las necesidades de nuestra solución. Aunque, si lo deseamos, podemos agregar a las colas un comportamiento similar al de los tópicos, interactuando con las propiedades de los mensajes y creando filtros para responder de forma distinta a cada uno de los mensajes que encontremos en la cola. Digo similar, pero esto no llega ni a la mitad de los tópicos, pues aquí no estaríamos empleando ningún sistema de colas distinto al provisto por Azure y se sigue compitiendo por recibir los mensajes.

Desde el SDK 2 tenemos la posibilidad de recibir mensajes de una forma event-driven a través del OnMessage, método que encapsula todo el loop infinito que sondea la cola en espera de nuevos mensajes. Con este modelo dirigido por eventos ya podemos imaginarnos el por qué de los Reactive Extensions. Los Reactive Extensions nos permiten crear secuencias observables de distintas formas, así podemos trabajar con estos datos asíncronos de una forma más sencilla y escribiendo un código más declarativo, fácil de leer y mantener. Así que manos a la obra.

Lo primero es crear la secuencia observable a partir del metodo OnMessage, aquí, no sabía bien cual era el mejor enfoque (aún me pierdo con todos los operadores de Rx), así que pregunté a los colegas y terminé con un código de la forma:

            IObservable<BrokeredMessage> observable = Observable.Create<BrokeredMessage>(
                observer =>
                {
                    _queueClient.OnMessage(observer.OnNext, new OnMessageOptions());
                    return Disposable.Empty;
                }).Publish().RefCount();

El código anterior tiene tres partes importantes, lo primero es acudir al método Create (En la sobrecarga con IDisposale) para crear la secuencia observable y devolver un IDisposable. Esta secuencia observable se creará a partir de la implementación de una función “subscriptora” (El primer parámetro del método). Si dejamos hasta ahí y empezamos a vincular subscriptores (más de uno) a la secuencia observable, el método OnAction se ejecutará cuantas veces se agregué un subscriptor, lo que provocaría una excepción con el mensaje: The method ‘OnMessage’ or ‘OnMessageAsync’ has already been called. Lo que nos lleva al segundo punto importante del código, el operador Publish, éste retornará un IConnectableObservale<T>, que nos permitirá compartir la subscripción (y no solo la secuencia observable), si! compartir el OnMessage que registramos en el Create. Finalmente, hacemos uso del RefCount, que nos retorna un IObservable<T> a partir del IConnectableObservable<T> y manejará por nosotros la conexión y desconexión de los subscriptores.

Teniendo “claro” como se genera la secuencia observable solo queda usar los conocimientos de linq para crear nuevas secuencias observables y subscripciones. En este ejemplo separaremos comandos y consultas y ejecutaremos algo (impresión en pantalla) con esta información. Algo de la forma:

            var comandos = observable.Where(
                message =>
                {
                    object obj;
                    return message.Properties.TryGetValue("IsCommand", out obj) && (bool)obj;
                }).Subscribe(
                x => //OnNext
                {
                    Console.ForegroundColor = ConsoleColor.DarkYellow;
                    Console.WriteLine(x.GetBody<string>());
                },
                x => Console.WriteLine(x.Message), //OnError
                () => Console.WriteLine("Complete")); //OnComplete

Y tenemos como salida:

image

El código completo esta en este gist.

Como vemos la implementación con Reactive Extensions nos permite escribir un código más declarativo, un poco más fácil de mantener y de leer, con lo que ya sabemos de Linq.

Espero les sea de utilidad.

Hasta el próximo post

Azure Service Bus Queue Client + Reactive Extensions