Mostrando entradas con la etiqueta Capa de Presentación. Mostrar todas las entradas
Mostrando entradas con la etiqueta Capa de Presentación. Mostrar todas las entradas

lunes, 3 de agosto de 2009

Aplicaciones multilenguaje ASP.NET MVC

El desarrollo de capa de presentación Web en .NET ha cambiado desde que Microsoft se decidió a realizar una implementación oficial del conocido patrón MVC en la plataforma ASP.NET y la (amplia) comunidad de desarrolladores de ASP.NET lo asumió con todos los cambios que implica.

Y esta implementación ha calado rápido y profundo, (ya se está desarrollando la segunda versión) ha llegado a la gente acostumbrada a Frameworks ligeros de desarrollo web que incluían sus propias implementaciones de MVC. Entre otras ventajas, ha ayudado a estandarizar la organización de los archivos del sitio y refinar las responsabilidades (Controladores -> Procesos de interfaz de usuario, workflow de la aplicación... Vistas –> mostrar la información de manera amigable y recoger los datos, validaciones de formato...).

Si deseamos acogernos a la implementación de Microsoft de dicho patrón deberemos ir encajando los problemas la funcionalidad de toda la vida sobre estas nuevas reglas de juego.

Uno de los requisitos clásicos más general es que nuestra aplicación disponga de características de globalización / localización. La idea general es la de siempre, evitar incluir ninguna referencia explicita a una determinada cultura y jugar con las características de globalización que nos ofrece el .NET Framework. Pero como ya sabemos, en Web todo se complica un poquito más. Al correr sobre un protocolo sin estado como HTTP recae sobre nuestra aplicación recordar la cultura especificada por el usuario entre las distintas peticiones.

Entonces tenemos clara la misión:

Mostrar los elementos de la UI acordes a la cultura que el usuario especifique.

Suena fácil, no? Pues lo es, si somos un poco cuidadosos…

(Por cierto, os dejo al final del post el código de la solución para que os lo podáis descargar y probar.)

1 - Estableciendo las bases, recursos a los ficheros de recursos.

Vamos a ir realizando un ejemplo paso a paso para evitar perdernos:

  1. Creamos un nuevo proyecto ASP.NET MVC Web Application.
  2. Incluimos en el proyecto la carpeta de App_GlobalResources.
  3. Incluimos un par de ficheros de recursos de acuerdo a la configuración de recursos por idioma que deseamos incluir.

    La convención de nomenclatura de los mismos dice que la primera parte del archivo es el nombre base. La segunda especifica la cultura (puede también ser solo el lenguaje que la forma). Esta última parte es opcional, entendiéndose como el juego de recursos predeterminado en caso de no exista.

    Es responsabilidad del .NET Framework enlazar automáticamente (en base a los ficheros de recursos especificados) el fichero adecuado con a la cultura especificada en actual hilo de ejecución.

    Por ejemplo, creamos el fichero predeterminado (en Inglés) y otro para español sin tener en cuenta el país. (MLTest.resx y MLTest.es.resx (para cualquier cultura con lenguaje español))
  4. Incluimos en el conjunto de ficheros de recursos todos los recursos que nuestra aplicación va a necesitar.
    En nuestro caso solo uno con nombre "GREETING".
  5. Incluimos desde una vista ( por ejemplo Views/Home/Index.aspx) la forma de consumir el recurso:

    <%
    =Resources.MLTest.GREETING%>

    Fuertemente tipado, Intellisense, detección de errores en tiempo de compilación...(da gusto verlo!!)

Bueno, pues ya tenernos una aplicación que nos muestra los recursos en base a la cultura del Framework instalado (en el servidor Web en este caso) que se encarga de especificar la cultura del hilo de ejecución.

Ya es algo, pero no es lo que queremos, verdad?

(El que dude que revise la misión que hemos puesto arriba)

2- Estableciendo la cultura del hilo en ejecución

Ya sabemos que debemos establecer la cultura del hilo de ejecución para que el Framework cargue el fichero de recursos adecuado, pero ahora la pregunta es cuando?

Recordar que cada petición empieza de nuevo y no recuerda las anteriores ( HTTP es stateless). Indagando un poco en la MSDN podemos ver que la clase Controller (de la que derivan nuestros propios controllers) de la implementación del MVC de Microsoft especifica dos métodos virtuales que nos van a resultar muy interesantes:

  • OnActionExecuting
    Se ejecuta antes de la llamada a cualquier Action Method.
  • OnActionExecuted
    Se ejecuta después de la llamada a cualquier Action Method. (obvio, no?)

Como debemos de establecer la cultura cada vez que nos realicen una llamada a cualquier action method de cualquier controlador, lo ideal es crear un controlador base del cual hereden todos los demás y sobrescribir en el método OnActionExecuting el mecanismo para restablecer la cultura. Nuestros controladores harán así transparente al resto de la aplicación las particularidades de trabajar con estado. También podríamos usar un atributo aplicado sobre la clase base y en la implementación del atributo especificar que implementa el interfaz IActionFilter si lo queremos hacer un poco mas AOP que OO.

En la implementación que os dejo utilizo un atributo con el que decoro la clase base (Al gusto!!)

En el ejemplo:

La clase base de los controladores:

[SetCulture]
public class BaseController : Controller
{
...
}

Y en la implementación de la clase del atributo programo como establecer la cultura seleccionada en el hilo activo:



public class SetCultureAttribute : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext
actionContext)
{
CultureHelper.SetCulture(GetCurrentCulture
(actionContext), actionContext.
HttpContext.Session)
}
...


Para almacenar la cultura especificada entre las llamadas podemos emplear diferentes mecanismos, en este ejemplo el flujo de trabajo será el siguiente:



  1. Revisar si tiene alguna cookie de nuestro sitio que especifique la cultura.

  2. Comprobar la variable de sessión de la cultura.

  3. En caso de no disponer de ninguna de las dos informaciones anteriores, preguntar por las culturas configurados en el navegador y presentar la interfaz de usuario en base a la primera coincidencia que se produzca.


En la implementación que os dejo del proyecto podéis consultar los métodos que he creado para realizar dichas acciones, son muy sencillos y podéis modificarlos al gusto.

Los principales métodos son:



  • GetCurrentCulture de la implementación del atributo, que consulta la información previamente almacenada en base al flujo que hemos comentado.

  • CultureHelper.SetCulture que recibe la cultura a asignar y el objeto intrínseco Session Object y carga cultura en el hilo actual de ejecución.


Aquí nos aparece otro problema.(Que raro, eh?)

Si el usuario es la primera vez que accede a la Web no tiene cookie ni session establecida.

Para intentar que la aplicación sea lo más “inteligente” posible, recogemos del navegador las culturas que tiene especificadas y las cruzamos con una sección especifica del fichero de configuración creada a tal efecto.

Por ejemplo, si el usuario tiene Chino e italiano necesito determinar si mi aplicación dispone un algún fichero de recursos que da soporte a alguno de los mismos. Para ello me creo una section en el fichero de configuración y un handler que lo soporte que especifique una lista de culturas soportadas y su orden de aplicación. (Así vemos también como poder realizar esta tarea.)

Observar que la tarea no es tan sencilla como coger el idioma y asignarlo al hilo y dejar que el Framework resuelva. (si no lo puede resolver que aplique la predeterminada) puesto que si el usuario tiene, entre todos los lenguajes del navegador, uno que tenemos implementado nos lo estaríamos saltando. (Por ejemplo Chino, Italiano, Español, Ruso)


En el fichero de configuración especificamos:

<
configSections>
...
<section name="EnabledLanguages" type="AndoniArroyo.MLTest.
Controllers.Infrastructure.Configuration.EnabledLanguagesSection,
AndoniArroyo.MLTest"
/>

</
configSections>
...

además incuimos la citada sección:


<!--Especifica el conjunto de culturas con el que trabaja la aplicación. La última es la cultura por defecto-->
<EnabledLanguages>
<Languages>
<add title="Spanish" code="es-ES" />
<add title="English" code="en-GB" />
</Languages>
</EnabledLanguages>


Para que la aplicación pueda manejar esta section debemos implementar el handler que la maneje:


namespace AndoniArroyo.MLTest.Controllers.Infrastructure.
Configuration
{
public sealed class EnabledLanguagesSection : ConfigurationSection
{
[ConfigurationProperty("Languages", IsDefaultCollection = true,
IsRequired = true)]
internal LanguageElementCollection EnabledLanguages
{
get { return (LanguageElementCollection)this["Languages"]; }
}
}
}

Estos lenguajes será los que tendrá en cuenta el método que intenta cargar el lenguaje desde la información recogida del navegador.


Pues ya casi lo tenemos.

Nos queda por ver como el usuario puede explicitamente modificar la cultura con la que desea trabajar en la aplicación. Visto todos lo anterior la cosa es fácil, no? Basta con crear un action method de modificación de la cultura y en la implementación del mismo llamar explícitamente al método de establecer cultura con el parámetro recibido. (CultureHelper.SetCulture pasándole la cultura especificada por el usuario y la referencia al objeto de sesión...)


Recapitulando, hemos visto unas cuantas cosas:



  • Hemos observado los mecanismos de globalización que nos da el .NET Framework y como extenderlos para nuestras necesidades en web.

  • Definido una política de establecer la cultura activa de la aplicación intentando que la aplicación se adapte al usuario.

  • El punto donde cubrir las características de sin estado del HTTP para establecer la cultura especificada por el usuario.

  • Hemos creado secciones personalizadas en el fichero de configuración y hemos visto como manejarlas.


Y como extra lo prometido el ejemplo con todas estas ideas en acción.


lunes, 1 de diciembre de 2008

WPF Binding del control al propio DataContext

Alguna vez me han proguntado como se puede relacionar el propio objeto del DataContext en un Binding. Esto que en principio puede parecer un poco raro suele ser muy habitual a la hora de establecer Converters en WPF (IValueConverter).

Bueno pues el tema es de lo más simple. Supongamos que deseamos hacer un Binding del objeto del contexto a la propiedad Text de un TextBlock:

Text="{Binding Mode=OneWay, Converter={StaticResource andoniBlogConverter}}

*Notese que no se establece Path ni por propiedad ni a través de la sobrecarga del constructor de la clase Binding

Por supuesto se necesitaría un converter puesto que el objeto del contexto no es de tipo string (necesario para la propiedad Text seleccionada)

Por supuesto necesitariamos el Static Resource:

andoniNamespace:AndoniBlogConverter x:Key="andoniBlogConverter"


y la referencia al namespace en la declaración del UserControl:

xmlns:andoniNamespace="clr-namespace:Andoni.blog.ExampleConverter"

Espero que les sea de ayuda.

Bueno, un saludo



lunes, 29 de septiembre de 2008

Microsoft + JQuery ! (con el MVC la Santa Trinidad)

jQuery va a formar parte de la plataforma oficial de desarrollo de Microsoft.

Por cierto, jQuery es una librería de Javascript que permite simplificar la gestión de manipulación de documentos HTML a través del D.O.M. gestión y manejadores de eventos, animaciones...

Esta unión va a permitir que jQuery se distribuya con el Visual Studio. La idea es incluirla sin modificaciones (a ver si es verdad!!) de tal manera que se permita al equipo de desarrollo de jQuery continuar su propio avance de manera independiente.

Además los desarrolladores de Visual Studio podrán disponer de intellisense para jQuery (hay rumores que comentan que estará disponible como una descarga gratuita independiente en unas semanas)

Personalmente he desarrollado algunos proyectos con jQuery y la experiencia ha sido positiva por lo que me parece una conjunción positiva.

Un saludo.

martes, 27 de mayo de 2008

Razones para evitar la programación en cliente en Aplicaciones Web

Existen algunas razones para intentar evitar la programación en el lado del cliente:
  • Aislamiento
    El código del lado del cliente no puede acceder a los recursos del servidor. Por ejemplo, una aplicación que se ejecuta en el cliente no tiene una manera sencilla de leer un fichero o interactuar con una base de datos del servidor (al menos no sin caer en problemas con la seguridad y compatibilidad de navegadores)

  • Seguridad: Los usuarios finales pueden ver el código de cliente. (Y cuando digo pueden es SIEMPRE pueden solo varia el tiempo que tienes que invertir en poder ...) Por lo tanto un usuario malintencionado al entender como funciona la aplicación puede "trampearla" más a menudo.
Un saludo

miércoles, 12 de marzo de 2008

Arquitectura de capa de presentación: Del ayer al mañana (II)

Como los ordenadores se hicieron mas sofisticados, las tarjetas perforadas dejaron paso a los terminales que adquirían y mostraban los resultados del servidor central.


Arquitectura de petición-respuesta basada en terminal

Para un procesamiento muy básico este modo de proceder era útil, pero los requisitos de las aplicaciones informáticas son cada vez más sofisticados, como el correo electrónico, news... para los cuales dichos terminales se han quedado pequeños.

Pese a esto, es común observar en grandes almacenes o grandes tiendas de venta al público, que las cajas siguen funcionando con terminales poco más sofisticados que estos, donde todos los procesos se ejecutan en el servidor pese a tener un PC en la caja con la suficiente capacidad para desarrollar otras tareas. (Upsizing? otro día hablaré de ello...)

Si recuerda algun viejo programa de correo electrónico para terminales como "eml" también recordará la necesidad de tres o cuatro dedos extra para hacer cualquier cosa útil.

Desde el punto de vista de la arquitectura, este escenario no difiere mucho del de las tarjetas perforadas, pero unifica el punto de contacto con el mainframe y cambia el medio del papel a los electrones. Pese a que la arquitectura no cambia la implementación si lo hace y fue este cambio de implementación un factor que encaminó la mejora global de la experiencia de usuario, un hecho que continua hoy en día.

martes, 11 de marzo de 2008

Arquitectura de capa de presentación: Del ayer al mañana (1)

El interfaz de usuario a evolucionado a lo largo de un camino cíclico que abarca desde los clientes ligeros hasta los pesados y vuelta a empezar.

Por un lado, los requisitos de los usuarios así como las demandas de funcionalidad extra conducen al tipo de cliente pesado. Por otro lado, los requisitos y demandas relativas a la facil instalación, actualización y mantenimiento nos conducen a los clientes más ligeros.

Con cada iteración el interfaz de cliente "más ligero" gana funcionalidad y el "más pesado" se vuelve más fácil de instalar, actualizar y mantener.

El interfaz de usuario original fue probablemente el cliente más ligero de todos. Las tarjetas perforadas que se enviaban al servidor central que devolvía los resultados impresos.

Arquitectura simple de petición-respuesta