<input type="text" … />
con el atributo Maxlength
establecido, con objeto de evitar la introducción de textos más extensos de lo indicado en las restricciones StringLength
del Modelo.En este post vamos a implementar la misma funcionalidad, pero sobre áreas de texto (tag
<textarea>
), de forma que podamos también limitar el número de caracteres introducidos en este tipo de controles. Como ya sabemos en este caso es algo más complejo, puesto que tenemos que solventar mediante scripts la ausencia del atributo maxlength
que sí teníamos disponible en los cuadros de texto convencionales.Por cierto, no sé qué razones llevarían a no incluir al
textarea
el atributo maxlength
en las versiones anteriores de (X)HTML, pero parece ser que HTML5 va a enmendar esta situación permitiéndolo como atributo válido. De hecho, a día de hoy ya hay algunos navegadores que lo soportan, como las últimas versiones de Chrome y Firefox; IE9 y Opera, por ejemplo, todavía no lo han incorporado… en fin, un poco lo de siempre :-(1. Situación de partida
En la actualidad, podemos decorar una propiedad del Modelo con el atributoStringLength
para limitar el número máximo de caracteres que acepta, y utilizar el atributo DataType
para indicar que su edición por defecto debe realizarse mediante un control multilínea, es decir, un área de texto. Hasta ahí, todo es correcto y muy cómodo de implementar.Sin embargo, debido a la ausencia del atributo
maxlength
en los <textarea>
, el usuario puede introducir en tiempo de edición todo el texto que desee. Más adelante, al salir del campo o al aceptar el formulario, el mecanismo de validación mostrará el error de que se han sobrepasado el número de caracteres admitidos y no lo dejará continuar, lo cual, como podréis entender, es una auténtica aberración.Desde el punto de vista del usuario, el número de caracteres no es un dato conocido para él mientras teclea, la limitación no es indicada en ningún punto, y además no obtiene feedback alguno en el momento que ésta ha sido superada, por lo que lo único que puede hacer cuando aparece el error es comenzar a eliminar texto e ir probando periódicamente hasta que alcanza el tamaño deseado. Usabilidad cero, o menos :-(
2. ¿Qué pretendemos conseguir?
<textarea>
en HTML) con una capacidad limitada de texto, que será la especificada en las propiedades del Modelo mediante el uso del atributo StringLength
.Como una imagen vale más que todo lo que yo os pueda describir en varios párrafos, a la derecha tenéis una captura de pantalla del resultado que vamos a conseguir.
Crearemos un “control” reutilizable que nos permitirá, de forma muy sencilla y casi totalmente automática, generar editores en áreas de texto limitadas que informarán al usuario en todo momento del número de caracteres que puede introducir, y que no permitirá sobrepasar la longitud especificada en el atributo
StringLength
.3. Limitando los <textarea> mediante scripts
Está claro que las funcionalidades que vamos a añadir a las áreas de texto necesitan ser implementadas mediante scripting: es necesario contar los caracteres introducidos por el usuario, eliminar lo que exceda del tope especificado, mostrar información… Afortunadamente, podemos encontrar en la red muchos scripts que realizan estas tareas y que podemos utilizar de forma directa.El plugin Limit para jQuery es un buen ejemplo: simple, sin grandes ambiciones, pero funciona bien y es muy ligero, así que le delegaremos el trabajo sucio. El siguiente código muestra cómo podemos limitar la longitud para un
textarea
concreto, y mostrar el contador de caracteres que faltan para completarlo:<textarea id="myTextarea"></textarea> <div>Quedan <span id="charsLeft"></span> caracteres</div> <script type="text/javascript" src="/scripts/jquery.limit.js"></script> <script type="text/javascript"> $('#myTextarea').limit('140', '#charsLeft'); </script>
Si conseguimos generalizar este código e inyectar el tamaño indicado con el atributo
StringLength
en la propiedad a editar, tendremos el trabajo hecho :-)El primer paso que vamos a dar, de momento, es descargar el plugin y copiarlo a la carpeta /Scripts del proyecto, bajo el nombre jquery.limit.js.
4. El enfoque
Vamos a implementar este componente basándonos en las plantillas de edición de ASP.NET MVC, un mecanismo del framework que nos facilita la creación de potentes interfaces de introducción de datos, muy reutilizables y fáciles de construir.Cuando desde una vista utilizamos el helper
Html.EditorFor()
para generar el editor de una propiedad concreta, el framework acude a la carpeta /Views/Shared/EditorTemplates en busca de una plantilla (=vista parcial), que genere el interfaz de edición de dicha propiedad. Normalmente la plantilla que se intenta localizar depende del tipo de dato de la propiedad a editar. Así, por ejemplo, si la propiedad es
DateTime
se intenta localizar en la citada carpeta una vista parcial llamada DateTime.cshtml (o .aspx si trabajamos con el motor Webforms); si es un int
se buscará la plantilla int.cshtml, y así sucesivamente. Puedes ver un ejemplo en otro post, donde explicaba paso a paso cómo implementar un editor de fechas bastante apañado.Aquí, sin embargo, vamos a utilizar otro enfoque. Existen distintas fórmulas para indicar al framework la plantilla de edición exacta que queremos utilizar para editar una propiedad:
- en la propia llamada a
Html.EditorFor()
, que dispone de una sobrecarga en la que podemos indicar el nombre de la plantilla a utilizar para la edición del dato, - o bien, sobre la propiedad del modelo usando el atributo
[UIHint]
, suministrándole como parámetro el nombre de la plantilla.
[DisplayName("Descripción ampliada")]
[StringLength(140), Required]
[UIHint("LimitedTextArea")]
public string Descripcion { get; set; }
Y de esta forma, al generar el editor para la propiedad
Descripcion
desde cualquier vista con Html.EditorFor()
, renderizaremos el contenido de la plantilla LimitedTextArea.cshtml disponible en /Views/Shared/EditorTemplates. Más sencillo imposible.5. LimitedTextArea.cshtml
El código de esta plantilla de edición no es demasiado extenso, creo que se puede entender de un vistazo:@model string
@{
// Obtenemos el valor de la anotación MaxLength del modelo...
int maxLength = 0;
var stringLengthAdapter = ViewContext.ViewData.ModelMetadata
.GetValidators(ViewContext.Controller.ControllerContext)
.OfType<StringLengthAttributeAdapter>()
.FirstOrDefault();
if (stringLengthAdapter != null)
{
var parms = stringLengthAdapter
.GetClientValidationRules()
.First()
.ValidationParameters;
if (parms.ContainsKey("max"))
{
maxLength = (int)parms["max"];
}
}
// Obtenemos ahora el id y name del <textarea> a editar...
var id = ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId("");
var name = ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName("");
}
@Html.TextAreaFor(model => model, new { cols=50, rows=5 })
@if (maxLength > 0)
{
<div class="textarea-chars" id="msg-textarea-@(id)">
Máximo @maxLength caracteres
</div>
<script type="text/javascript"> $(function () {
var limit = $('#@id').limit;
if (limit) {
$("#msg-textarea-@id")
.html("Quedan <span id='left@(id)'></span> caracteres");
$('#@id').limit('@maxLength', '#left@(id)');
}
});
</script>
}
En la primera parte, el bloque de código de servidor, se extrae el número máximo de caracteres permitidos desde los metadatos de la propiedad que estamos editando, obteniendo para ello la información indicada en el atributo
StringLength
. A continuación simplemente se obtienen también los id
y name
que serán asignados al <textarea>
para poder referenciarlo más adelante.Seguidamente, mediante una llamada al helper
Html.TextAreaFor()
se genera el área de texto de la forma habitual.Por último, siempre que
maxlength > 0
, es decir, que se haya indicado un límite de caracteres concreto, se inserta un <div>
para mostrar los mensajes, y se inicializa el plugin jQuery Limit, que se encargará del resto.En vista del código, seguro que entendéis las tres posibilidades que se pueden dar a la hora de renderizarse este editor:
- Si la propiedad decorada con el atributo
UIHint("LimitedTextArea")
no tiene establecido unStringLength
, aparecerá el área de edición como siempre, no se realiza ninguna operación adicional. - En caso contrario, es decir, si se ha especificado un tamaño máximo, se comprueba la disponibilidad del plugin Limit:
- si no ha podido detectarse, por ejemplo porque hemos olvidado incluir la biblioteca jQuery.limit.js en la página, simplemente aparecerá un mensaje indicando al usuario el número máximo de caracteres, aunque no se activará ninguna de las funcionalidades ni automatismos previstos,
- si el plugin ha sido cargado, se activa en el
<textarea>
que hemos creado.
¡Y esto es todo! Simplemente creando este archivo LimitedTextArea.cshtml en la carpeta /Views/Shared/EditorTemplates, decorando con el atributo
UIHint("LimitedTextArea")
las propiedades que queremos editar en el editor multilínea controlado, e incluyendo en la vista la biblioteca jQuery.Limit.js
, lo tendremos funcionando.… aunque esto último todavía podríamos mejorarlo un poco… ;-)
6. Punto extra: ¡Incluye el script por mí, por favor!
Sobre la marcha se me ocurre una mejora que creo que podría resultar interesante: ya que detectamos cuándo el plugin jQuery Limit no está disponible, podríamos insertar un código de tratamiento para este caso e intentar incluirlo de forma automática, asumiendo que se encuentra en la carpeta/Scripts
. El tema consistiría únicamente en sustituir el bloque <script> del código anterior por el siguiente:
<script type="text/javascript">
$(function () {
var limit = $('#@id').limit;
if (!limit) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "@Url.Content("~/scripts/jquery.limit.js")";
$("head").append(script);
limit = $('#@id').limit;
}
if (limit) {
$("#msg-textarea-@id")
.html("Quedan <span id='left@(id)'></span> caracteres");
$('#@id').limit('@maxLength', '#left@(id)');
}
});
</script>
Fijaos que el único cambio es la inserción del primer
if
, en el que se detecta la inexistencia del plugin, y se añade un tag <script>
al DOM para cargarlo. De esta forma, no tendremos que acordarnos de hacer la inclusión de la biblioteca en cada vista donde lo utilicemos :-)Podéis descargar el código completo del editor y una demo para ASP.NET MVC 3 desde Skydrive, y veréis lo bien que queda y lo fácil que es de utilizar.
Publicado en: Variable not found.
Publicado por José M. Aguilar a las 9:59 a. m.
Etiquetas: asp.net, aspnetmvc, desarrollo
Estos son los enlaces publicados en Variable not found en Facebook y Twitter desde el domingo, 29 de mayo de 2011 hasta el domingo, 05 de junio de 2011. Espero que te resulten interesantes. :-)
- jQuery ASP.Net MVC Controls
Fecha: 02/06/2011 - Prop function in jQuery 1.6.
Fecha: 02/06/2011 - What ASP.NET MVC Could Learn From Rails, by Justin Etheredge
Fecha: 02/06/2011 - Carga diferida en Entity Framework 4.1, por Sebys.
Fecha: 02/06/2011 - Gráficos de Google en ASP.NET MVC con un Helper personalizado, por Oscar Sotorrio
Fecha: 02/06/2011 - Referencing Routes in ASP.NET MVC The Rails Way, by Rob Conery.
Fecha: 01/06/2011 - jQuery Code Snippets for Visual Studio 2010
Fecha: 30/05/2011 - Automatic JS, CSS versioning to update browser cache when files are changed.
Fecha: 30/05/2011 - ADO.NET team blog: EF 4.1 Validation.
Fecha: 30/05/2011 - Into and let in LINQ ( Let vs Into)
Fecha: 30/05/2011 - Huy Nguyen: Dynamically Generated Images with ASP.NET MVC
Fecha: 30/05/2011 - Custom ASP.NET MVC route class with catch-all segment anywhere in the URL
Fecha: 29/05/2011
Y no olvides que puedes seguir esta información en vivo y en directo desde Variable not found en Facebook, o a través de Twitter.
Publicado en: Variable not found
Observad el siguiente código de vista, bastante trivial:
@Html.EditorFor(model=>model.Nombre)
Intuitivamente podemos estar seguros de que el control será generado con “Nombre” como nombre de campo e identificador. El código creado será algo así, poco más o menos:<input type="text" name="Nombre" id="Nombre" />
De esta forma, podríamos acceder al contenido del cuadro de texto por ejemplo con el script mostrado a continuación, que se encarga de convertir su contenido en mayúsculas cuando pierde el foco:<script type="text/javascript">
$("#Nombre").blur(function () {
var mays = $(this).val().toUpperCase();
$(this).val(mays);
});
</script>
Y funcionar, funcionaría… algunas veces ;-)Hay muchos escenarios en los que no está tan claro el identificador que será asignado, sobre todo si estamos generando controles de edición para propiedades de tipos complejos, en vistas parciales, o cuando estamos creando editores personalizados para utilizar con
Html.EditorFor()
. Este último caso además es especialmente complicado, puesto que desconocemos por completo el nombre de la propiedad para la cual será utilizado.Lo que suelo hacer en estos casos es utilizar estos dos pequeños helpers,
Html.IdFor()
y Html.NameFor()
, que permiten obtener el nombre e identificador asignado a una propiedad del Modelo accediendo a sus metadatos:public static class HtmlHelpers
{
public static string IdFor<TModel, TProperty>(this HtmlHelper<TModel> html,
Expression<Func<TModel, TProperty>> expression)
{
string propiedad = ExpressionHelper.GetExpressionText(expression);
return html.ViewData.TemplateInfo.GetFullHtmlFieldId(propiedad);
}
public static string NameFor<TModel, TProperty>(this HtmlHelper<TModel> html,
Expression<Func<TModel, TProperty>> expression)
{
string propiedad = ExpressionHelper.GetExpressionText(expression);
return html.ViewData.TemplateInfo.GetFullHtmlFieldName(propiedad);
}
}
De esta forma, podemos reescribir el script anterior asegurando la corrección del identificador utilizado indicando la propiedad mediante una expresión lambda con tipado fuerte, como solemos hacer en los helpers de edición:
<script type="text/javascript">
$("#@Html.IdFor(model=>model.Nombre)").blur(function () {
var mays = $(this).val().toUpperCase();
$(this).val(mays);
});
</script>
Y aunque es menos frecuente, también podemos encontrarnos con la necesidad de obtener el nombre de campo del formulario (atributo “name”) a utilizar para un control de edición. Por ejemplo, si queremos construir de forma manual las etiquetas, podemos establecer el nombre utilizando Html.NameFor()
:<input type="text"
id="@Html.IdFor(model=>model.Nombre)"
name="@Html.NameFor(model=>model.Nombre)" />
Espero que os sea de utilidad.Publicado en: Variable not found.
En el caso concreto de la capa Vista, prácticamente nadie escribe ejemplos utilizando Razor y VB.NET, por lo que los desarrolladores que siguen optando por este lenguage para trabajar sobre ASP.NET MVC (e incluso WebPages) lo tienen más complicado para entender y utilizar código existente. Además, a diferencia de lo que podría pensarse, la codificación no es exactamente igual en ambos lenguajes, y a veces no es fácilmente inferible, lo cual añade además un poco de dificultad al usar VB.
En este post vamos a mostrar una tabla de equivalencias entre C# y VB.NET a la hora de codificar distintas construcciones que utilizamos frecuentemente al crear vistas con Razor.
Archivos de vistas o páginas Razor
C# | VB.NET |
nombrearchivo.cshtml | nombrearchivo.vbhtml |
Definición del tipo de datos del Modelo
C# | VB.NET |
@model Persona | @ModelType Persona |
Importación de espacios de nombres
C# | VB.NET |
@using MyApp.Models | @Imports MyApp.Models |
Definición de clase base de la vista
C# | VB.NET |
@inherits ClaseBase | @Inherits ClaseBase |
Bloque de código
C# | VB.NET |
@{ // Código C# } | @Code ' Código VB.NET End Code |
Instrucciones de bloque (*)
C# | VB.NET |
@if(a > b) { // Hacer algo } | @If a > b Then ' Hacer algo End If |
Salida de expresión
C# | VB.NET |
Hola, @Model.Nombre | Hola, @Model.Nombre |
Mezcla de código y marcado
C# | VB.NET |
@if(a > b) { <p>A es mayor que B</p> } | @If a > b Then @<p>A es mayor que B</p> End If |
@if(a > b) { @: una línea de texto o HTML } | @If a > b Then @: una línea de texto o HTML End If |
@if(a > b) { <text> Aquí va texto o HTML </text> } | @If a > b Then @<text> Aquí va texto o HTML </text> End If |
Definición de secciones de un Layout
C# | VB.NET |
@section Encabezado { <h3>Este es el encabezado</h3> } | @Section Encabezado <h3>Este es el encabezado</h3> End Section |
Creación de helpers
C# | VB.NET |
@helper Tabla(int num) { <ul> @for (int i = 1; i < 11; i++) { <li>@num x @i = @(num*i) </li> } </ul> } | @Helper Tabla(num As Integer) @:<ul> For i = 1 To 10 @<li>@num x @i = @(num * i) </li> Next @:</ul> End Helper |
Bloques de funciones
C# | VB.NET |
@functions { int suma(int a, int b) { return a + b; } } | @Functions Function suma(a As Integer, b As Integer) As Integer Return a + b End Function End Functions |
Razor templated delegates
C# | VB.NET |
@{ Func<dynamic, object> bold = @<strong>@item</strong>; } Uso: @bold("Esto en negrilla") | @Code Dim bold = Function(item As Object) @<strong>@item</strong> End Function End code Uso: @bold("Esto en negrilla") |
@helper Lista( Func<dynamic, HelperResult> templ, params dynamic[] args) { foreach(dynamic item in args) { @templ(item) } } Uso: <ul> @Lista(@<li>@item</li>, 1, 2, 3.5, "hola", DateTime.Now) </ul> | @Helper Lista(templ As Func(Of Object, HelperResult), ParamArray args() As Object) For Each item In args @templ(item) Next End helper Uso: <ul> @Lista(Function(item As Object) @<li>@item</li> End Function, 1, 2, 3.5, "hola", DateTime.Now) </ul> |
Espero que no se me haya quedado por detrás ninguna de las construcciones habituales. De todas formas, si detectáis alguna ausencia, no dudéis en avisarme y lo incluyo en este mismo post.
Publicado en: Variable not found.
- Custom ASP.NET MVC route class with catch-all segment anywhere in the URL
Fecha: 29/05/2011 - There's a new T4 editor in town... The (free) Devart T4 Editor for Visual Studio 2010 and/or 2008
Fecha: 28/05/2011 - ReSharper 6 Enhances the JavaScript Experience, by Hadi Hariri.
Fecha: 26/05/2011 - Qué buena pinta! RT Lluis Franco: UALA!!! Essential Diagram for ASP.NET MVC.
Fecha: 26/05/2011 - HTML5 and Accessibility
Fecha: 26/05/2011 - Ugo Lattanzi: Add IE 9 Pinned Sites, Dynamic Jump Lists & Notifications to MVC Razor Views
Fecha: 26/05/2011 - Phil Haack: Blogged: How to easily bin deploy ASP.NET MVC 3.
Fecha: 26/05/2011 - Entity Framework 4.1 Code First, Silverlight, and Shared Models with REST+JSON.
Fecha: 26/05/2011 - Genial Scott Hanselman: Globalization, Internationalization and Localization in ASP.NET MVC, JavaScript and jQuery.
Fecha: 26/05/2011 - Security Issue in ASP.NET MVC3 JsonValueProviderFactory
Fecha: 24/05/2011 - Tomasz Pęczek: Blogged: "jqGrid and ASP.NET MVC – Batch updates"
Fecha: 24/05/2011 - Entity Framework 4.1 – Supporting Enums.
Fecha: 24/05/2011 - Quick Look at Reverse Engineer DB into Code First Classes.
Fecha: 24/05/2011 - IIS 7.0/7.5’s Hidden Tool. Run-time page request performance data.
Fecha: 24/05/2011 - Scott Hanselman: NuGet Package of the Week #7 - ELMAH (Error Logging Modules and Handlers) with SQLCompact
Fecha: 24/05/2011
Publicado en: Variable not found
El veloz murciélago hindú comía feliz cardillo y kiwi. La cigüeña tocaba el saxofón detrás del palenque de paja. 1234567890.
Pues hoy debe ser mi día de suerte: me he topado casualmente con una entrada de la Wikipedia donde desentrañan el significado de tan inquietante frase, que parece haber sido creada por un perturbado mental o bien por los mismísimos guionistas de Lost (o ambas cosas al mismo tiempo) ;-)
Y la explicación es bien simple: se trata de un pangrama, también llamado frase holoalfabética, que es una frase que contiene todas las letras que componen el alfabeto de un idioma.
Explicado esto, seguro que ya cobra algo de sentido que sistemas operativos como Windows o Linux la utilicen a la hora de mostrar cómo lucen las distintas tipografías. Dado que los pangramas incluyen todas las letras, permiten que el consumidor de las mismas se haga una idea de cómo queda la fuente en un texto. El hecho de incluir al final el número es también por el mismo motivo, así como suele ser frecuente verlo acompañado de la misma frase utilizando exclusivamente mayúsculas.
También suelen utilizarse para practicar mecanografía con objeto de ejercitar todos los dedos de las manos.
Pero la gracia y mérito de los pangramas consiste en construir frases con sentido que incluyan el mayor número de letras del alfabeto en el menor espacio, como las siguientes:
- Cada vez que me trabo, Félix paga un whisky añejo (39 letras, 100% del alfabeto)
- ¡Ávida cigüeña floja!, pibonazo quemó whisky extra (41 letras, 100% del alfabeto)
- Incluso en otros idiomas:
- The quick brown fox jumps over the lazy dog (Inglés; El veloz zorro marrón salta sobre el perro perezoso)
- Pa's wijze lynx bezag vroom het fikse aquaduct (Holandés; El sabio lince de papá observó devotamente el formidable acueducto)
- Um pequeno jabuti xereta viu dez cegonhas felizes (Portugués; Una curiosa tortuguita vio diez felices cigüeñas)
- Portez ce whisky au vieux juge blond qui fume (Francés; Lleve este whisky al viejo juez rubio que fuma)
- (puedes ver muchas más en el artículo de la wikipedia)
En fin, cosas curiosas que hay por el mundo…
Publicado en: Variable not found.
Pero además de ser una utilidad imprescindible, una de sus características más interesantes es que puede ser extendido con suma facilidad. En este post vamos a ver cómo crear un plugin sencillo que nos permita mostrar en Glimpse información que nos interese sobre nuestra aplicación.
Y como tiene que ser, lo vamos a ver desarrollando un ejemplo paso a paso.
0. Objetivo
Para no distraernos de la intención de este post, que es ver cómo se crean plugins para Glimpse, vamos a desarrollar una extensión muy sencilla, un visor que nos permita observar información sobre el usuario actualmente conectado.El plugin, al que llamaremos “Authentication” se instalará en el panel de Glimpse y mostrará información como la siguiente:
- Nombre de la aplicación
- Nombre del usuario conectado
- Último login
- Roles del usuario en el sistema
Para obtener esta información de forma rápida utilizaremos los sistemas integrados de membresía y funciones (membership y roles) de ASP.NET. Obviamente, podéis utilizar los mismos conceptos que vamos a ver para implementar visualizadores más específicos de vuestra aplicación, como objetos de sesión, caché, e incluso información obtenida desde bases de datos :-)
1. Instalación de Glimpse
Quiero imaginar que todos estáis ya utilizando Nuget a tope (¿verdad?), por lo que no me detendré a explicar cómo instalar esta indispensable herramienta, y comenzaremos directamente instalando Glimpse.Como ya describí en el otro post, la instalación de Glimpse es trivial utilizando la consola de Nuget:
Each package is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Some packages may include dependencies which are governed by additional licenses. Follow the package source (feed) URL to determine any dependencies.
Package Manager Console Host Version 1.3.20419.9005
Type 'get-help NuGet' to see all available NuGet commands.
PM> install-package glimpse
[...]
Successfully installed 'log4net 1.2.10'.
Successfully installed 'NLog 1.0.0.505'.
Successfully installed 'Castle.Core 1.2.0'.
Successfully installed 'Castle.DynamicProxy 2.2.0'.
Successfully installed 'Glimpse 0.80'.
Successfully added 'log4net 1.2.10' to DemoGlimpse.
Successfully added 'NLog 1.0.0.505' to DemoGlimpse.
Successfully added 'Castle.Core 1.2.0' to DemoGlimpse.
Successfully added 'Castle.DynamicProxy 2.2.0' to DemoGlimpse.
Successfully added 'Glimpse 0.80' to DemoGlimpse.
PM>
2. Añadir una referencia al proyecto
Para poder desarrollar nuestro plugin para Glimpse necesitamos añadir al proyecto una referencia al ensambladoSystem.ComponentModel.Composition
. Esto es así en la versión actual (v0.80 beta), no sé si más adelante se suprimirá esta molestia que, en cualquier caso, es bastante leve.3. Implementar el plugin
Vamos a pasar directamente a crear el plugin, que veréis que es bastante simple. En primer lugar, añadimos al proyecto una clase que implemente el interfazIGlimpsePlugin
, e implementamos los siguientes miembros definidos en el mismo:void SetupInit(HttpApplication application)
, que contiene código de inicialización que será invocado al cargarse el plugin (una vez por petición), si así se configura expresamente. Por defecto este método ni siquiera será llamado.
string name {get; }
, que debe retornar el nombre de la pestaña que aparecerá integrada en el interfaz de Glimpse.public object GetData(HttpApplication application)
, que retornará un objeto cualquiera con la información que debe ser mostrada por Glimpse en nuestra pestaña. Este objeto será serializado como JSON, y el valor de sus propiedades, tenga la estructura que tenga, mostrada en el panel de la herramienta.
GlimpsePlugin
. La implementación completa del plugin sería la mostrada a continuación.
using System;
using System.Collections.Generic;
using System.Web;
using System.Linq;
using Glimpse.Net.Extensibility;
using System.Web.Security;
namespace Prueba.Utils
{
[GlimpsePlugin()]
public class AuthenticationPlugin: IGlimpsePlugin
{
public object GetData(HttpApplication application)
{
if (!application.Context.User.Identity.IsAuthenticated)
return new { Usuario = "No autenticado" };
MembershipUser usr = Membership.GetUser();
string[] roles = Roles.GetRolesForUser(usr.UserName);
return new
{
ApplicationName = Membership.ApplicationName,
Username = usr.UserName,
Email = usr.Email,
LastLoginDate = usr.LastLoginDate,
Roles = roles.Any()? roles: new[] { "Ninguno" }
};
}
public string Name
{
get { return "Authentication"; }
}
public void SetupInit(HttpApplication application)
{
}
}
}
Observad que el método principal
GetData()
, puede retornar cualquier tipo de objeto (incluidos los de tipo anónimo, como es el caso). Además, fijaos que el tipo de retorno es distinto cuando estamos autenticados y cuando no lo estamos; Glimpse simplemente mostrará en el panel correspondiente el objeto que le suministremos, sea del tipo que sea, recorriendo sus propiedades y visualizándolas de la forma apropiada.4. ¡Lo tenemos!
Voilá, ya tenemos nuestro plugin para Glimpse funcionando y ofreciéndonos la información que necesitamos:Obviamente el ejemplo es muy simple, pero seguro que sois capaces de imaginar escenarios en los que os puede resultar de gran utilidad: ver el contenido de objetos específicos de vuestra aplicación, mostrar trazas personalizadas, consumo de recursos, observar peticiones Ajax, y un larguísimo etcétera.
Siguiendo el ejemplo anterior e implementando a vuestra conveniencia el método de obtención de datos lo tendréis listo sin apenas esfuerzo.
Publicado en: Variable not found.
- Gonzalo Pérez: Utilizando LocalStorage de HTML5
Fecha: 20/05/2011 - Gunnar Peipman: Using DebugView to catch debug output of .NET program
Fecha: 20/05/2011 - Steven Moseley: Elmah for Glimpse – Best of both worlds
Fecha: 19/05/2011 - Unai Zorrilla: EF 4.1 EDMXWriter.
Fecha: 19/05/2011 - Más de Unai: EF 4.1 Power Tools CTP1
Fecha: 19/05/2011 - "ADO.NET EF 4.1", el nuevo libro de Unai Zorrilla.
Fecha: 17/05/2011 - Elijah Manor: "Adding a site wide Log In Features to an ASP.NET MVC Web Site" by JoeStagner ASP.NET MVC
Fecha: 17/05/2011 - Luis Ruiz Pavón: [Tips & Tricks] IE Developer Tools no se muestran a veces.
Fecha: 16/05/2011 - Code First EF 4.1 : Missing “ADO.NET DbContext Generator” VS Template.
Fecha: 16/05/2011 - Code First EF 4.1 : Building Many to Many Relationship.
Fecha: 16/05/2011 - Securing ELMAH with Independent HTTP Authentication.
Fecha: 16/05/2011 - Built-in GZip/Deflate Compression on IIS 7.x. , by Rick Strahl.
Fecha: 16/05/2011 - How to Enable ASP.NET MVC App Localization using T4 to generate .resx resource files
Fecha: 16/05/2011 - John Katsiotis: Step by step instructions to deploy ASP.NET MVC 3 to Windows Azure
Fecha: 16/05/2011
Publicado en: Variable not found
Como la definen sus autores, Glimpse es al servidor lo que Firebug al cliente. En la práctica, se trata de un componente que nos permite obtener desde el navegador una visión en profundidad de lo que está ocurriendo en el servidor durante la ejecución de las peticiones. Brevemente, lo que vamos a conseguir ver con Glimpse es:
- Información sobre configuración de la aplicación
- Información sobre el entorno
- Métodos ejecutados
- Metadatos detallados de las clases del modelo
- Información enviada en la petición
- Tabla de rutas completa
- Valores retornados en las variables de servidor
- Información almacenada en variables de sesión
- Vistas completas y parciales cargadas en la respuesta
- Peticiones Ajax realizadas desde el cliente
Observad que Glimpse ocupa toda la zona inferior, y aunque podría parecer lo contrario, es puro HTML y no un complemento del navegador como ocurre con las herramientas anteriormente citadas. Todo lo que vemos es creado por el componente cliente de Glimpse e introducido en la página actual :-)
Arquitectura
Glimpse está compuesto por los siguientes elementos:- Glimpse Server Module, el componente encargado de recolectar información de depuración en servidor.
- Glimpse Client Side Viewer, encargado de obtener la información de depuración desde el módulo servidor y mostrar el interfaz de usuario que permite su estudio.
- Glimse Protocol, el protocolo que permite el intercambio de información entre servidor y cliente.
Sin embargo, el objetivo final de Glimpse es bastante más ambicioso: aunque la implementación actual está muy ligada a la tecnología .NET, su arquitectura modular hará posible la aparición de componentes de servidor para cualquier tecnología, como PHP o RoR. Éstos se comunicarán con el lado cliente mediante Glimpse Protocol, que es ajeno a la tecnología utilizada en servidor.
El componente de cliente, por su parte, es capaz de obtener datos de depuración generados en servidor y representados en Glimpse Protocol, y mostrarlo integrado en la página. Es importante saber que este cliente está construido sobre jQuery, por lo que la página donde vayamos a utilizarlo debe cargar esta biblioteca de scripts previamente para que todo funcione correctamente.
El diseño del visor se ha conceptualizado, además, para que sea extensible: será posible añadir plugins que den soporte a características avanzadas, como pestañas para mostrar información específica de plataformas o CMS como Sharepoint u Orchad, autenticación para acceder a la información de depuración, etc.
Instalación y puesta en marcha de Glimpse
Este apartado, que describe la instalación de Glimpse en nuestro proyecto ASP.NET MVC o Webforms, podría ser realmente extenso de no ser por Nuget. Glimpse depende de otros paquetes como Castle.Core, Castle.DynamicProxy o Log4net y la descarga e instalación manual de estos componentes podría acabar con la paciencia de cualquiera. Afortunadamente esta joya nos va a poner la tarea bastante fácil :-)Desde la consola Nuget, accesible a través del menú Herramientas > Library Package Manager > Package Manager Console, podemos dejarlo listo en segundos simplemente introduciendo el comando
install-package
como sigue:Each package is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Some packages may include dependencies which are governed by additional licenses. Follow the package source (feed) URL to determine any dependencies.
Package Manager Console Host Version 1.3.20419.9005
Type 'get-help NuGet' to see all available NuGet commands.
PM> install-package glimpse
[...]
Successfully installed 'log4net 1.2.10'.
Successfully installed 'NLog 1.0.0.505'.
Successfully installed 'Castle.Core 1.2.0'.
Successfully installed 'Castle.DynamicProxy 2.2.0'.
Successfully installed 'Glimpse 0.80'.
Successfully added 'log4net 1.2.10' to DemoGlimpse.
Successfully added 'NLog 1.0.0.505' to DemoGlimpse.
Successfully added 'Castle.Core 1.2.0' to DemoGlimpse.
Successfully added 'Castle.DynamicProxy 2.2.0' to DemoGlimpse.
Successfully added 'Glimpse 0.80' to DemoGlimpse.
PM>
Tras introducir la orden de instalación, Nuget descargará e instalará el paquete Glimpse, junto con todas sus dependencias, en nuestro proyecto.
El siguiente paso es acudir a la pantalla de configuración de Glimpse con objeto de activarlo para nuestro sitio web. Para ello, simplemente tenemos que lanzar nuestro proyecto y acudir a la dirección /glimpse/config, donde encontraremos el botón de activación “Turn Glimpse On”:
Como en el caso de Firebug, Developer Tools de IE y otros, esta información aparece dividida en pestañas, como se puede apreciar en la siguiente captura de pantalla:
A continuación se describe lo que podemos encontrar en cada una de estas pestañas, exceptuando “Binding”, que aún no está implementada, y “Glimpse warnings”, que muestra avisos internos de la propia herramienta.
Pestaña “Config”
En esta pestaña tendremos acceso a la configuración de la aplicación, o en otras palabras, obtendremos una vista estructurada de determinados parámetros indicados en el web.config. Así, tendremos acceso detallado a parámetros definidos en la secciónappSettings
, cadenas de conexión, configuración del elemento customErrors
y al modo de autenticación utilizado en el sitio. Pestaña “Environment”
Aquí podemos consultar diversos datos sobre el entorno en el que se está ejecutando nuestra aplicación, como el nombre de la máquina, sistema operativo, versión del framework, servidor web, ensamblados cargados, etc.Pestaña “Execution”
Esta interesante pestaña nos muestra información sobre los métodos que han sido ejecutados para procesar la petición, tanto en el controlador como en los filtros aplicables a la acción. Curiosamente, también encontraremos en esta pestaña los métodos no ejecutados en éstos.Como se puede observar en la captura, también ofrece información sobre el orden de ejecución y el tiempo de proceso consumido por de cada uno de estos métodos, lo que nos puede ayudar a detectar cuellos de botella.
Pestaña “Metadata”
Facilita el análisis de los metadatos asociados a la información suministrada desde el controlador a la vista a través de la propiedadModel
. En ellos veremos, con todo lujo de detalles y por cada propiedad las descripciones textuales, reglas básicas de validación y formato, el tipo de datos, etc.Pestaña “Remote”
Aunque no parece funcionar de forma correcta, o al menos no de la forma en que uno podría esperar, esta pestaña permite acceder a las peticiones realizadas por clientes del sitio web, y lanzarlas de nuevo para observar su información de depuración a través del resto de pestañas.Pestaña “Request”
Permite observar los datos enviados en la petición actual en cookies, como campos de formulario, o en la querystring.Pestaña “Routes”
Para mi gusto, una de las pestañas más interesantes que ofrece Glimpse. Nos permite consultar la tabla de rutas completa, conocer cuál de las rutas ha sido elegida para procesar la petición, y los valores para cada uno de sus parámetros.Si ya habéis estado trabajando con la última versión de RouteDebugger de Phil Haack, esta herramienta os sonará bastante, pues se parece bastante. Si todavía no lo habéis hecho, no dejéis de probarla, pues os ayudará mucho cuando os encontréis con peticiones que acaban siendo rutadas aparentemente de forma incomprensible.
Pestaña “Server”
En esta sección tendremos acceso a las variables HTTP enviadas en la respuesta del servidor.Pestaña “Session”
Aquí se nos ofrece información muy interesante: los datos almacenados en variables de sesión para el usuario actual. Podremos ver la clave de búsqueda en la colección Session, el valor almacenado y el tipo de éste.Pestaña “Trace”
En esta pestaña tendremos acceso a la información de depuración generada por la propia aplicación utilizando los métodos de trazas disponibles enSystem.Diagnostics
. Es decir, podemos llamar desde desde el código al método Trace.WriteLine()
para escribir mensajes de depuración que después consultaremos desde este punto.Pestaña “Views”
Esta pestaña nos permite, en primer lugar, consultar información sobre el proceso de localización de las vistas, tanto completas como parciales, por parte de los distintos view engines registrados en el sistema. Como se puede observar en la siguiente captura de pantalla, las dos líneas destacadas con las vistas utilizadas para componer la página actual, el resto son los intentos de localización que se han realizado de forma infructuosa.Además, en cada una de las vistas procesadas tendremos acceso a la información que le está siendo suministrada desde el controlador a través de los diccionarios
ViewData
y TempData
, y de la propiedad Model
:Pestaña “XHRequests”
En ella podremos consultar en tiempo real las peticiones Ajax que están siendo realizadas. Pulsando el enlace “launch” asociado a cada una de ellas, podemos acceder a la información de depuración del proceso de dicha petición accediendo a la pestaña que nos interese.Una curiosidad: si en esta pestaña lo que aparecen son las peticiones Ajax, ¿por qué la han llamado “XHRequest”, y no “Ajax”? La respuesta la encontramos en un twit de uno de sus creadores: en la versión actual del cliente las pestañas salen ordenadas alfabéticamente, y no quería que ésta fuera la primera :-D
Glimpse plugins
Aunque aún es pronto (recordad que Glimpse es todavía una beta), la arquitectura pluggable del producto hará posible la aparición de extensiones del producto que nos permitirán obtener más datos de depuración, y adaptarlo a plataformas específicas.Un ejemplo lo podemos encontrar ya en “Glimpse Dependencies Plugin”, un componente que podemos instalar directamente desde Nuget (“
install-package glimpse-dependencies
”). Se trata de una extensión que añade una pestaña adicional en el cliente, que ofrece información sobre el proceso de resolución de dependencias que utilizan el sistema incorporado en MVC 3:Desinstalación de Glimpse
Una vez hemos acabado con Glimpse, podemos desinstalarlo con total sencillez, de la misma forma que lo instalamos. Utilizaremos esta vez el comandouninstall-package
, e indicaremos que deseamos eliminar también las dependencias que instalamos con él en su momento:PM> Uninstall-Package -RemoveDependencies glimpse
Successfully removed 'Glimpse 0.80' from WebSCCB.
Successfully removed 'Castle.DynamicProxy 2.2.0' from WebSCCB.
Successfully removed 'Castle.Core 1.2.0' from WebSCCB.
Successfully removed 'NLog 1.0.0.505' from WebSCCB.
Successfully removed 'log4net 1.2.10' from WebSCCB.
Successfully uninstalled 'Glimpse 0.80'.
Successfully uninstalled 'Castle.DynamicProxy 2.2.0'.
Successfully uninstalled 'Castle.Core 1.2.0'.
Successfully uninstalled 'NLog 1.0.0.505'.
Successfully uninstalled 'log4net 1.2.10'.
PM>
Tras esta operación, nuestro proyecto volverá a estar igual que como lo teníamos. No me canso de decirlo: Nuget es una maravilla :-)
Conclusión
Sin duda, Glimpse es una herramienta realmente interesante para los que trabajamos con ASP.NET MVC y, aunque actualmente en menor medida, con Webforms, que nos ofrecerá una perspectiva de lo que ocurre en el servidor hasta ahora imposible de obtener de forma tan sencilla, y que nos puede ayudar bastante depurar nuestros sistemas.La facilidad y limpieza con la que la instalamos y desinstalamos en nuestros proyectos gracias a Nuget hace que sea realmente cómodo contar con ella cuando la necesitemos y volver al estado original cuando ya no sea necesaria.
Por poner alguna pega, aún se trata de un producto en beta, y que aún no implementa todas las funcionalidades. También se echa en falta algo de documentación que ayude a averiguar el significado de determinadas opciones, pero entiendo que esto llegará conforme vaya madurando.
En fin: descargadlo, probadlo, y veréis como os gusta.
Publicado en: Variable not found.
Publicado por José M. Aguilar a las 10:00 a. m.
Etiquetas: asp.net, aspnetmvc, depuración, herramientas
Bien, pues hace ya cinco años de esto. Se dice pronto, eh? :-)
Para los más jóvenes diré que este mensaje de error era bastante habitual cuando programábamos con aquel Basic primitivo de los años 80 e intentábamos acceder a una variable no definida. Por cierto, aquellos de mi quinta que queráis tener un momento nostalgia, o los que simplemente tengáis curiosidad por trastear con él, podéis encontrar aquí un emulador online de esta maravilla.
Y tras todo ello, ya sólo faltaba lo más difícil: intentar crear contenidos razonablemente decentes, darlo a conocer, conseguir visitas, suscriptores, comentarios… y en ello estamos :-)
Como en otras ocasiones, aprovecho para resumir los distintos periodos por los que hemos ido pasando desde aquél momento para, justo después, comentar este último año que acabamos de finalizar.
Año 1: la travesía del desierto (mayo 2006 - mayo 2007), caracterizado principalmente por ser yo el único visitante (poco más o menos) al blog, por aquellos tiempos disponible a través de la dirección jmaguilar.blogspot.com y con la imagen de una de las plantillas de Blogger.
La siguiente gráfica muestra los primeros meses de vida del blog, donde era extraño superar las tres visitas al día. Esos picos que se ven probablemente sería yo mismo haciendo pruebas:
También es curioso ver los primeros veinte días sin datos estadísticos: era tan ignorante –o tenía tan pocas esperanzas de que fuera a entrar alguien- que ni siquiera se me ocurrió activar ningún sistema de seguimiento hasta 20 días después de la inauguración oficial de la bitácora :-D
- Año 2: el despegue (mayo 2007 - mayo 2008), durante el cual se produjeron múltiples novedades, como la adquisición del dominio variablenotfound.com, la difusión de feeds a través de Feedburner, la creación del diseño actual, y la inclusión en agregadores y comunidades como Planeta Código, Planet WebDev y Geeks.ms.
Este segundo año lo acabamos con un incremento del 2.500% en el número de visitas (la verdad, tampoco lo tenía muy difícil al partir casi de cero ;-)), debido en gran parte a algunas avalanchas procedentes de Menéame.
Pero lo que me parecía más increíble era algo a lo que, curiosamente, no había prestado demasiada atención y que, sin embargo ahora, es uno de los parámetros que más valoro: los suscriptores a los feeds. Acabábamos el año con unos increíbles 380 suscriptores.
- Año 3: la consolidación (mayo 2008 – mayo 2009), periodo sin grandes cambios en el que Variablenotfound.com continuó creciendo, aunque no de forma tan espectacular como lo había hecho anteriormente.
Se incrementaron en un 75% el número de visitas procedentes de buscadores, debido a que comenzaba a notarse la antigüedad del blog, el número de referencias externas y su posicionamiento en buscadores, compensando así la práctica ausencia de “meneos” y otro tipo de avalancha.
Según elinestableinestimable feedburner, los suscriptores a feeds superaron este año los 600, lo que suponía un aumento casi del 100% respecto al periodo anterior. Impresionante.
- Año 4: la variable social (mayo 2009 – mayo 2010), se continuaba la estabilidad en cuanto al número de visitas, aunque seguían creciendo a un ritmo importante. Superábamos los 900 suscriptores a los feeds, y rozábamos los 100.000 usuarios únicos absolutos al año, sumando las visitas a variablenotfound.com y su eco en geeks.ms.
La gran novedad del periodo fue la inclusión del componente social, creando una página en Facebook y comenzando a retransmitir vía Twitter. Acabábamos el año pasado con cien seguidores en el primero, y más de cincuenta en el segundo.
Año 5: La variable física
La principal novedad de este año ha sido el hecho de haber salido de la cueva, y haber podido introducir la componente física en esta ecuación.La asistencia al TTT/Open Day me permitió ponerles cara y voz a fenómenos a los que admiraba y llevaba siguiendo desde hacía mucho tiempo, y con los que fue un auténtico placer poder compartir unas horas. Más recientemente, la gira Make Web Not War en la que he tenido el placer de participar también me ha dado la oportunidad de saludar personalmente a amigos a los que sólo conocía virtualmente, y de echar unos ratos excelentes con un grupo estupendo. Y finalmente, todo esto me ha permitido entrar en contacto con la gente de CartujaDotNet, el grupo de usuarios .NET de Sevilla, donde espero participar y colaborar de forma presencial.
Entrando ya en otro orden de asuntos, Variable not found ha continuado su trayectoria ascendente incrementando el número de usuarios de forma considerable en todos los canales abiertos:
- Un 30% de incremento en el número de visitas y páginas vistas en variablenotfound.com, donde estamos ya estabilizados por encima de las 10.000 visitas/mes. Si sumamos las consultas de estos contenidos desde geeks.ms, nos ponemos ya en 15.000 visitas/mes.
- Feedburner indica cerca de un 25% de incremento, superando los 1.100 suscriptores a los feeds del sitio.
- Conseguimos cerca de un 300% de incremento en el número de followers en Twitter, que rondan los 230 en este momento.
- Un 100% de incremento de seguidores en Facebook, donde superamos ya los 200 amigos.
En cuanto a los navegadores, Firefox sigue siendo el más usado para acceder a variablenotfound.com, aunque el uso de Chrome se ha duplicado en el último año en detrimento de IE y del propio Firefox. Este trío concentra el 95% de los usuarios.
Curiosamente, el 7% de los usuarios que usan Internet Explorer siguen con la versión 6 :-O. Pero bueno, el año pasado a estas alturas era tres veces más, así que no podemos decir que la campaña IE6 count down no esté surtiendo efecto…
Otro dato interesante es que el año pasado Windows XP era utilizado prácticamente por el 60% de los visitantes, y ahora rondamos el 45%. Windows 7 ha crecido hasta cerca del 40%.
Afortunadamente sigo disfrutando al escribir cada post, al revisar los comentarios (pocos, pero haberlos haylos), al descubrir las referencias externas, comprobar que los posts son útiles de vez en cuando a alguien, y al ver que el número de amigos de variablenotfound.com continúa creciendo. Y desde luego, sigue sin haber mejor recompensa que ver que estáis ahí después de tanto tiempo.
¡Mil gracias a todos! Y por supuesto, espero que sigáis ayudándome a buscar la variable un año más ;-)
Publicado en: Variable not found
Publicado por José M. Aguilar a las 10:26 a. m.
Etiquetas: aniversario, autobombo, blogging, variablenotfound, variablenotfound.com