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!
lunes, 16 de noviembre de 2009

¡Programadiseñadores al poder! Ya sabemos lo que suele ocurrir cuando los programadores diseñamos interfaces de usuario ;-). Para seguir profundizando en este curioso e inevitable fenómeno, Ian Voyce ha publicado hace unas semanas el divertido post The 7 signs your UI was created by a programmer, en el que recoge pistas que nos ayudarán a determinar si el interfaz de la aplicación que usamos a diario está creado por un diseñador experto en usabilidad, o ha sido el propio desarrollador el encargado de dichas tareas.

Bueno, en realidad se trata más bien de una lista con malos hábitos en el diseño de interfaces, y no necesariamente creados por programadores, pero ciertamente me he reconocido en alguno de los puntos y me han hecho gracia. Los cito y comento a continuación:

  • Iconos de exclamación en cuadros de diálogo
    Efectivamente, cuando incluimos una exclamación en un cuadro de diálogo no pensamos en que es posible que el usuario vea el mensaje 50 veces al día, con lo que dejaría de ser ese mensaje asombroso y puntual que pensamos en un principio. Hoy en día, muchas aplicaciones utilizan un tono mucho más neutral y cercano para enviar mensajes al usuario.

  • Mensajes con dobles negaciones en cuadros de diálogo, del tipo “¿Seguro de que no quieres perder tus cambios?” -- Hmm… sí… digooo, no…  También son muy propias construcciones más complejas de la cuenta, paradójicas o inesperadas, como “Su mensaje no se ha enviado, ¿desea descartarlo?”, que ponen a prueba los reflejos de cualquier usuario.

  • Orden de tabulación inexistente, o incorrecto, que fuerzan a que la navegación se realice exclusivamente a golpe de ratón. Y especialmente en aplicaciones de entrada masiva de información, el cambio de teclado a ratón y viceversa es terrible. Y paraGroupboxes para todo.. porque mola hacernos conscientes de ello, nada como dar vacaciones un rato al animalito y ver lo bien que manejamos determinadas aplicaciones.

  • Groupboxes rodeándolo todo… porque hay sitios donde queda bonito, aunque no esté agrupando nada ;-). Me confieso: durante años, he rodeado de groupboxes todo lo que se me ponía por delante, porque me parecía más agradable a la vista. Ahora lo estoy dejando ;-)

  • IconoIconos creados en el IDE, je, este es otro de mis puntos fuertes: soy un fiera diseñando iconos desde el editor de Visual Studio. El problema es que, a pesar de lo orgulloso que estoy siempre de mis creaciones artísticas, no quedan bien e incluso introducen un cierto tufillo a cutre en las aplicaciones.

  • Utilización de grids, debido a lo que llama Voyce “la enfermedad de considerar que Excel es lo máximo en diseño de interfaces de usuario”, que fomenta la creación de rejillas con controles de lo más potentes y variopintos en las filas de datos (calendarios, gráficos, etc.).

  • Message Boxes no implementados, el equivalente en los interfaces de usuario a los TODO en el código fuente :-DD. Buena analogía. Algo parecidos serían los mensajes del tipo “Este mensaje no debería aparecer nunca” que alguna vez he encontrado por ahí, auténticos actos de rebeldía contra sus respectivos creadores.

  • Uso excesivo de cuadros de diálogo, incluso en situaciones en las que perfectamente podríamos obviarlos por no ofrecer información útil para el usuario, que no va a poder entender, o donde no se requieren decisiones por su parte.

    Los componentes necesarios han sido detectados. Accediendo...Esto es algo muy frecuente de ver. Por ejemplo, el otro día accediendo a una aplicación web para la que es necesario contar con certificado digital, me saltó la alerta que muestro a la derecha:

    Sí, probablemente los componentes Java necesarios para la autenticación y firma electrónica hayan sido detectados con éxito, pero… ¿realmente era necesario interrumpir mi navegación para informarme de ello? ¿Un usuario, sabrá interpretar el mensaje? Y en cualquier caso, ¿le aporta algo? No sé, no sé… me da que es un alert() creado por un desarrollador durante las pruebas y que al final se quedó ahí para la posteridad.

Hasta aquí las citadas en el post original, aunque siguiendo en la misma línea, puedo añadir algunas más de propia cosecha:

  • Cuadros de diálogo con mensajes y botones inconsistentes. Mensajes del tipo “¿desea cancelar la operación?” en cuyo pie aparece un botón “Aceptar” y otro “Cancelar” siempre me dejan pensando un rato. Si pulso aceptar, ¿estoy aceptando la cancelación, o justo lo contrario?… Muchas veces, parece que sólo podemos confiar en el azar.

  • Cuadros de mensaje con demasiados (o demasiados pocos) botones. No siempre atendemos a la Ley de Hick, que nos dice que “el tiempo que se tarda en tomar una decisión aumenta a medida que se incrementa el número de alternativas”, y eso nos lleva a crear cuadros de diálogo con muchos, demasiados, botones que el usuario debe estudiar.

    Y justo en el lado contrario, pero igualmente desconcertantes, son los cuadros de diálogo de introducción de datos sin ningún botón de aceptación, en los que su cierre (con la X de la esquina superior) implica el salvado de datos.

  • Interfaz descuidado. Y no hablo de bonito o feo, términos bastante subjetivos, sino de descuidado: controles sin alinear, con tamaños no proporcionales al contenido pretendido, sensación de desorden, faltas de ortografía… Esto no es un problema de interfaces creados por desarrolladores, sino creados por gente que no pone el más mínimo cariño en lo que hace, independientemente de su puesto de trabajo.

  • Interfaces monocromos, mi especialidad. Como inútil reconocido en temas de diseño, me considero absolutamente incapaz de crear algo medio aparente con más de un color (eso sí, empleando distintas tonalidades ;-)). Nunca he entendido la compleja lógica que hay detrás del “eso no pega con aquello”, que algunos/as parecen dominar de serie.

  • Controles con trastorno de personalidad. Un clásico es encontrar un grupo de checkboxes actuando como un grupo de radios. A ver, si sólo vamos a poder elegir una de las opciones, ¿por qué no usamos directamente el control específicamente ideado para ello? ¿Y un desplegable con las opciones "Activar" y "Desactivar"? ¿No sería más claro poner un checkbox?

Artículo original: The 7 signs your UI was created by a programmer

Publicado en: Variable not found.

miércoles, 11 de noviembre de 2009

Imaginemos el siguiente código HTML:

<input type="text" name="nombre" /><br />
<input type="text" name="nombre" /><br />

Si desde un servidor ASP.NET, como respuesta a un submit (o postback en webforms), queremos acceder a los valores del campo del formulario “nombre”, podemos utilizar el clásico Request[“nombre”] para obtener los valores de todos ellos separados por comas.

Así, si introducimos en el primer campo el valor “Juan” y en el segundo “Pedro”, Request["nombre"] contendrá “Juan,Pedro”. Esto podría ser suficiente para procesarlo troceando la cadena a partir de las comas, por ejemplo así:

string[] nombres = Request["nombre"].Split(',');
 
string n1 = nombres[0]; // n1 = "Juan"
string n2 = nombres[1]; // n2 = "Pedro"

Sin embargo, es obvio que esta técnica genera problemas si un usuario introduce en alguno de los campos una coma, el carácter que estamos considerando como separador de los elementos.

La forma correcta de hacerlo sería utilizando el método GetValues() sobre la propiedad Form de la petición, la cual nos devuelve un array con los valores de cada campo. Por ejemplo, si en el primer campo introducimos “Juan,Jose” y en el segundo “Luis”, el resultado sería el que sigue:

string[] nombres = Request.Form.GetValues("nombre");
string n1 = nombres[0]; // Juan,Jose
string n2 = nombres[1]; // Luis

En ASP.NET MVC, el sistema de bindings es capaz de facilitarnos aún más las cosas. Si en la vista existen diferentes campos llamados “nombre”, y el submit se realiza sobre una acción en cuyos parámetros aparece un array con la misma denominación, recibiremos los datos directamente:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Editar(string[] nombre)
{
    // nombre[0] -> "Juan,Jose"
    // nombre[1] -> "Luis"
}

Publicado en: Variable not found.

lunes, 9 de noviembre de 2009

imageSimone Chiaretta, desarrollador, MVP, bloguero y autor de un libro sobre ASP.NET MVC Framework, entre otros méritos, recoge en su recomendable bitácora Code Climber unas cuantas buenas prácticas a considerar cuando trabajamos en proyectos ASP.NET MVC, que cito y comento a continuación:

  1. Borra el AccountController. Siendo fieles al principio YAGNI, prácticamente lo primero que deberíamos hacer al crear un proyecto ASP.NET MVC es eliminar todo lo relativo al sistema de cuentas de usuario incluido por defecto en el mismo, que muy difícilmente nos será útil tal cual en proyectos reales.

  2. Aísla los controladores del mundo exterior, no dejes que dependan de elementos externos como HttpContext,  datos del reloj (hora/fecha actual), ni accedas directamente a bases de datos o elementos de configuración. Harán tu aplicación muy poco flexible y difícil de testear.

  3. Utiliza un contenedor IoC, te ayudará a gestionar las dependencias de los controladores, facilitando la sustitución de estos elementos de forma muy sencilla, sobre todo, vistas a la realización de pruebas.

  4. No utilices cadenas mágicas, dificultan la detección de errores. No es buena idea usar expresiones del tipo ViewData["variable"], las vistas tipadas son el mecanismo ideal para pasar información desde el controlador. También es peligroso el uso de los helpers que utilizan cadenas para referirse a nombres de controladores y acciones, y para ello existen alternativas basadas en expresiones lambda o plantillas T4 bastante potentes.

  5. Crea tus propias convenciones tomando como base las propuestas por el framework MVC. Por ejemplo, crea controladores y vistas base de las que heredar, e implementa en ellos aspectos comunes.

  6. Presta atención a los Verbos, a los verbos HTTP, claro ;-). Envía información desde formularios con el método POST y genera las vistas que muestran datos desde peticiones GET. Utiliza el patrón Post-Redirect-Get.

  7. DomainModel != ViewModel, el primero representa los datos y lógica del dominio, mientras que el segundo se diseña exclusivamente para satisfacer las necesidades de información de las vistas y normalmente serán estructuras planas, y principalmente strings, que es el tipo de datos que normalmente necesitan los controles de entrada y salida de datos.

  8. Usa ActionFilters para manejar los datos compartidos entre distintas acciones y controladores, dejando que éstos se centren en la tarea que deben realizar. Puedes utilizar filtros para cargar información común (por ejemplo, para componentes de una página maestra) y utilizar vistas parciales para mostrarla.

  9. No utilices el code-behind. Nunca. En mi opinión, el code-behind no es necesario (al menos no le he encontrado aún ninguna utilidad en MVC), y puede crear confusión si compartes tu código con otros desarrolladores que no esperarán encontrar nada ahí.

  10. Escribe HTML siempre que puedas. No utilices helpers que lo único que van a hacer es generar el código HTML por ti. Es decir, mejor usar <input type="submit" value="grabar" /> que <%=Html.Submit("grabar") %>.

  11. Si hay un if, escribe un helper. Las vistas deben ser fáciles de leer, y muchos bloques de códigos mezclados con marcado no facilita la tarea. Dado que en las vistas la lógica ha de ser necesariamente muy simple, probablemente puedas crear helpers que simplifiquen la composición del marcado en casos en los que hay condiciones if o bucles for .

  12. Elige el motor de vistas cuidadosamente, WebFormViewEngine no es el único ni, para Simone, el mejor de los existentes. De hecho, recomienda el uso de Spark, un motor de vistas libre en el que es el propio HTML el que controla el flujo de composición del interfaz de forma muy sencilla y potente:

  13. <viewdata products="IEnumerable[[Product]]"/>
    <ul if="products.Any()">
      <li each="var p in products">${p.Name}</li>
    </ul>
    <else>
      <p>No products available</p>
    </else>
     
    En mi caso, todavía no he encontrado motivos suficientes como para dar el salto a un motor de vistas distinto de WebForms. Sinceramente, no me parece tan aberrante ver código y marcado dentro del mismo archivo, siempre teniendo en cuenta que la lógica que debe aparecer en estos puntos debe ser siempre relativa a la presentación y, por tanto, simplísima.

Opiniones aparte, la verdad es que es un interesante grupo de consejos a tener en cuenta.

Fuente: 12 ASP.NET MVC Best Practices

Publicado en: Variable not found.

miércoles, 4 de noviembre de 2009

Cadenas Encuentro en el blog de Gunnar Peipman un post sobre el nuevo método string.IsNullOrWhiteSpace, aparecido en .NET Framework 4.0 Beta 2, cuya misión es retornar un booleano indicando si la cadena pasada como parámetro contiene un nulo, está vacío, o exclusivamente caracteres de espaciado (espacios, saltos de línea, tabulaciones, etc.), un escenario bastante frecuente cuando estamos, por ejemplo, validando formularios o cualquier tipo de datos de entrada.

Por si no podemos esperar hasta la llegada de la nueva versión del framework, Gunnar propone una solución temporal basada en crear un método de extensión sobre la clase string que realice la misma tarea:

 
public static class StringHelper
{
    public static bool IsNullOrWhiteSpace(this string s)
    {
        if (s == null)
            return true;
 
        return (s.Trim() == string.Empty);
    }
}
 
Como ya comenté por aquí hace tiempo, la ventaja que tiene utilizar esta técnica es que permite invocar el método sobre una instancia de cadena, aunque ésta sea nula, sin riesgo a errores:
 
string a = null;
string b = "    ";
string c = "\n";
string d = "Hey!";
Console.Write (a.IsNullOrWhiteSpace()); // True
Console.Write (b.IsNullOrWhiteSpace()); // True
Console.Write (c.IsNullOrWhiteSpace()); // True
Console.Write (d.IsNullOrWhiteSpace()); // False

Y para los fieles a Visual Basic .NET, ahí va el código equivalente:

Imports System.Runtime.CompilerServices
Public Module StringHelper
    <Extension()> _
    Public Function IsNullOrWhiteSpace(ByVal s As String) As Boolean
        If s Is Nothing Then
            Return True
        End If
        Return s.Trim() = String.Empty
    End Function
End Module

Publicado en: Variable not found.

lunes, 2 de noviembre de 2009

Hoy vamos a dedicar un rato a comentar una técnica que es considerada una buena práctica en el desarrollo de aplicaciones web: el patrón PRG o Post-Redirect-Get. Seguramente alguna vez lo haya citado por aquí, pero nunca lo había explicado en profundidad.

Por último, antes de entrar en materia, es conveniente indicar que lo que vamos a ver es válido para ASP.NET Webforms, ASP.NET MVC y, en general, para cualquier tecnología de construcción de sitios web que incluya componentes en servidor, puesto que se trata de una forma de hacer las cosas, no de la implementación de una solución. Ya en el último epígrafe veremos implementaciones concretas para Webforms y el framework MVC.

El problema

Es bastante habitual que al desarrollar aplicaciones para la web creemos un formulario de introducción de datos, y lo normal es que estos envíen los datos al servidor utilizando el verbo HTTP Post. Hasta aquí, bien.

Cuando desde el servidor se recibe una petición de este tipo normalmente se ejecuta un código, por ejemplo para almacenar la información en la base de datos y, de forma bastante frecuente, aprovechamos para enviar al cliente feedback de que su operación ha sido realizada con éxito. Y aquí es donde aparece el problema.

image Si el usuario, por esas ocurrencias que suele tener ;-), decide pulsar F5 o actualizar la página en el navegador, se van a producir dos efectos desagradables:

  • primero, se le mostrará al usuario un cuadro de diálogo informándolo de algo que difícilmente va a entender y que, en cualquier caso, le asustará bastante. El navegador le informa de que está realizando un reenvío completo de los datos del formulario.
  • segundo, una vez superado el escollo anterior, se volvería a ejecutar en servidor toda la lógica asociada a la recepción de datos del formulario con consecuencias, a veces terribles, como el almacenamiento de registros duplicados en la base de datos.

¿Cómo podemos evitar esto?

La solución: Post-Redirect-Get

El patrón PRG viene a indicarnos una forma de diseñar nuestras aplicaciones pensando en evitar los problemas descritos anteriormente, y, como veremos, es bastante simple. El procedimiento general a seguir sería:

  1. Recibimos la petición Post con los datos que ha introducido el usuario en un formulario.
  2. Ejecutamos la lógica asociada a la recepción de dicho formulario, por ejemplo, grabar en la base de datos.
  3. Enviamos al cliente una respuesta con código HTTP 30x (Redirect), indicando al agente de usuario que debe solicitar otra página, en la mostraremos un mensaje informando de que el proceso se ha realizado con éxito.
  4. El navegador obtiene dicha página mediante una petición de tipo Get.

En este punto, si el usuario decide (o el diablo que lleva dentro le ordena ;-)) refrescar la página, lo único que conseguirá será que su navegador vuelva a solicitar la página en la que le estamos informando de que el proceso ha sido satisfactorio. No se le muestra ningún cuadro de diálogo amenazante, ni se ejecuta la lógica de nuevo, ni hay ningún tipo de daños colaterales.

El siguiente diagrama, basado en el de la imprescindible Wikipedia, muestra gráficamente este proceso:

Secuencia en el patrón PRG

¿Un poco de código? Sí, por favor

ASP.NET WebForms

La abstracción sobre los protocolos montada por la tecnología Webforms hace más difícil reconocer los conceptos que estamos tratando, pero aún así es bastante sencillo aplicar el patrón.

En los formularios web, los postbacks se realizan a través de métodos Http POST, por lo que la implementación de la lógica y la redirección podremos realizarlas en la implementación del evento de servidor correspondiente, por ejemplo, el de pulsación de un botón:

protected void btnAceptar_Click(object sender, EventArgs e)
{
    if (Page.IsValid)
    {
        // Lógica 
        Cliente cliente = new Cliente(txtNombre.Text, txtApellidos.Text);
        GestorDeClientes.Salvar(cliente);
    
        // Redirección
        Response.Redirect("clientes.aspx", true);
    }
}

La página “clientes.aspx” podría ser ser el listado de clientes registrados en el sistema, por ejemplo.

ASP.NET MVC

En el framework MVC, por su cercanía a los protocolos, sí es fácil identificar los conceptos de petición y redirecciones. El siguiente código sería equivalente al anteriormente mostrado, pero enviando al usuario a la acción “Index”:

[HandleError]
public class ClientesController : Controller
{
    ... // Otras acciones
 
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Editar(Cliente cliente)
    {
        if (ModelState.IsValid)
        {
            GestorDeClientes.Grabar(cliente);
            return RedirectToAction("Index");
        }
        return View(cliente);
    }
}

Publicado en: Variable not found