Averiguar el valor del "compilation debug"

Ya hemos hablado de la importancia del gente a la que no le hace mucha gracia ni jugar con el machine.config ni con el deploymente retail. De hecho, es bastante habitual no tener acceso al machine.config (sobre todo en servidores compartidos).

De forma que lo que queremos es asegurarnos de que siempre que no no nos encontremos en desarrollo (es decir que estemos en localhost) nuestro debug sea false.

Pues ahí va el código autoexplicativo:

    /// <summary>
    /// Define si se puede tener activado el debug
    /// </summary>
    protected bool CanDebug()
    {
        if (!Request.Url.DnsSafeHost.Equals("localhost"))
        {
            // Si no estoy en localhost...

            // Cogemos el elemento system/compiltation, donde se encuentra el atributo "debug"
            System.Configuration.ConfigurationSection section =
                System.Configuration.ConfigurationManager.GetSection("system.web/compilation") as
                System.Configuration.ConfigurationSection;

            if (section == null)
            {
                // En caso de no existir, el valor por defecto es debug = false
                return true;
            }
            else
            {
                // En caso de existir la sección y su atributo "debug", debemos asegurarnos de que sea false
                return !Convert.ToBoolean(section.ElementInformation.Properties["debug"].Value);
            }
        }
        else
        {
            // Si estoy en localhost puedo tener el debug como quiera.
            return true;
        }
    }


De este modo averiguaremos si nuestro valor de "debug" es el correcto, tanto si estamos en localhost como si no.

El modo de esar esta información queda a gusto de cada uno, pero una opción "bestia" sería la de poner este código en algún lugar que seguro vayamos a visitar:

        if (!CanDebug())
            throw new Exception("No se puede tener activado el debug");



HttpModule: AntiSpamModule

Nunca me gustan las definiciones de conceptos tan técnicos como es el caso del HttpModule. Primero porque no sé definir bien las cosas, y segundo porque aunque lo supiera definir bien, lo que más aclara sigue siendo un buen ejemplo.

Para que nos hagamos a una idea, con los HttpModule's vamos a poder manejar eventos que  suceden antes que el Pre_Init de la página. Esperando que no se me olvide ninguno, y sin entrar a comentar qué sucede en cada uno de ellos, este es el orden en que se ejecutan dicho eventos:
  1. BeginRequest
  2. AuthenticateRequest
  3. PostAuthenticateRequest
  4. PostAuthorizeRequest
  5. ResolveRequestCache
  6. PostResolveRequestCache
  7. PostMapRequestHandler
  8. AcquireRequestState
  9. PostAcquireRequestState
  10. PreRequestHandlerExecute
Bien utilizados los HttpModules son muy útiles.

Todo muy bonito y todo muy bien... pero hasta el momento he dicho todo pero no he dicho nada. Vamos con un ejemplo, pero además un ejemplo muy útil: el AntiSpammer.

Desde hace tiempo vengo teniendo problemas en los foros de algunas de mis Webs. Algunos robots Spammers escribían diariamente "basura". Analizándolo a grandes rasgos, lo que hacen estos robots es hacer peticiones POST a una url definida con unos campos definidos. Hasta ahora había probado varios métodos,
como cambiar el nombre de los Textbox (y por tanto cambiaba el identificador de los campos o cambiar el nombre de la página (con lo que el robot ya no lo enviaba al sitio correcto).

Ninguno de esos métodos me funcionaba, pues los robots eran inteligentes... o simplemente  cogían los "Id" de todos los "input type text" y mandaban la basura con su nombre. Bueno, no lo sé, cuando algún día decida hacerme spammer lo analizaré mejor .

La cuestión es que lo único que realmente me ha funcionado ha sido o poner un Captcha (los odio) o permitir únicamente la inserción a usuarios registrados... pero ninguna de las dos soluciones me gusta.

Así que con la "inspiración" de resolver ese problema, he hecho este HttpModule.

Hacer un HttpModule es mucho más sencillo de lo que se piensa. Lo primero es crear una clase con la Interfaz "IHttpModule", la cual te obliga a tener dos métodos: "Init" y "Dispose".

En el método "Init" se recoge un parámetro del tipo "HttpApplication", desde el cual se tiene acceso a todas las variables típicas que se tienen desde una Página .aspx cualquiera. En nuesgtro ejemplo nos va a interesar recoger el Request, el Response.

Vayamos con el código, y pasamos a explicar qué vamos a hacer para tratar de evitar el Spam:

AntiSpamModule.cs
using System;
using System.Web;

namespace Subgurim.Tools
{
    public class AntiSpamModule : IHttpModule
    {
        public void Init(HttpApplication app)
        {
            app.BeginRequest += new EventHandler(AntiSpamFilter);
        }

        private void AntiSpamFilter(object sender, EventArgs e)
        {
            HttpResponse response = ((HttpApplication)sender).Response;
            HttpRequest request = ((HttpApplication)sender).Request;

            // 1.- Me me están mandando datos mediante POST
            // 2.- La llamada no proviene de ninguna otra página
            // 3.- La llamada proviene de otra página, pero que no está dentro de mi dominio
            if ((request.Form.Count > 0) &&
                    ((request.UrlReferrer == null) ||
                    (!request.UrlReferrer.Authority.Equals(request.Url.Authority, StringComparison.InvariantCultureIgnoreCase))))
            {
                try
                {
                    response.End();
                }
                catch (System.Threading.ThreadAbortException ex)
                {
                    // No hacer nada
                }
            }
        }

        public void Dispose()
        {
        }
    }
}

En el método Init vamos a indicar que cuando se lance el evento "BeginRequest" (el primero de todos) se llame al método AntiSpamFilter.

Dentro del AntiSpamFilter recogemos (vemos que es muy sencillo) el Response y el Request, y aquí es donde trataremos de localizar a los robots Spammers con tres sencillas reglas:
  1. Me me están mandando datos mediante POST
  2. La llamada no proviene de ninguna otra página
  3. La llamada proviene de otra página, pero que no está dentro de mi dominio
En caso de ser así, haremos, sin contemplaciones un Response.End(), con lo que paramos el ciclo de vida, y de ahí no pasa nadie. Nos sólo evitamos que nos Spammeen, sino que le estamos ahorrando a nuestro servidor un montón de cosas.

Ponemos un try - catch para manejar el ThreadAbortException, que se lanza siempre que se hace un Response.End.

En lugar de un Response.End, podría ser buena idea lanzar una HttpException 404, engañando al robot haciéndole creer que la página no existe.

Ya tenemos programado nuestro HttpModule... como véis es muy sencillo y no tiene otra historia que un clase implementando una interfaz.

Ahora nos queda configurar nuestra aplicación para que, en cada llamada, ejecute ese HttpModule. Como siempre, lo haremos desde nuestro web.config. Dentro del elemento System.Web debemos poner el elemento httpModules:

    <system.web>
        <httpModules>
            <add name="AntiSpamModule" type="Subgurim.Tools.AntiSpamModule"/>
        </httpModules>
    </system.web>

Con "name" con el nombre de la clase y type con el namespace (si está dentro del directorio App_Code) o el nombre del assembly si lo tienes en otro proyecto.

OJO: esas tres reglas básicas no han sido testeadas al 100%. Debería funcionar sin problemas, pero no sé, por ejemplo, si los robots de los buscadores (como GoogleBot o YahooSlurp) utilizan o utilizarán llamadas POST con parámetros para indexar mejor los contenidos... vamos, que me lavo las manos de lo malo que pueda ocurriros (aunque repito que a mí me ha funcionado sin problemas).



Introducción a la conexión a bases de datos

En este videotutorial hago una introducción a la conexión a bases de datos en ASP.NET.

Su nivel es muy básico, por lo que está dirigido a los programadores que se están iniciando.

En el videotutorial se creará una base de datos con SQLServer Express con una sencilla tabla de sólo dos registros. Además se crean 2 procedimientos almacenados, uno de inserción y otro de selección.

Posteriormente se configurará un SqlDataSource con ambos procedimientos almacenados, con un Detailsview para la inserción de datos y un Gridview para mostrarlos.




Ajax UrlTester


Todo webmaster que se precie debe seguir una regla de oro básica: cuando realizas un cambio en la Web, debes visitar las páginas donde se han hecho los cambios para comprobar que todo va correcto.

Esto, aparentemente tan lógico y trivial, no es tan sencillo como parece. Si estamos hablando de una o dos páginas no hay problema, pero hay ocasiones en que los cambios realizados son complejos y pueden implicar 20, 30 o 40 páginas.

Con esta idea en mente, y sin buscar antes en Google por si había ya algo parecido (de hecho aún no lo he mirado y no sé si lo haré), se me ocurrió hacer un script que visitara las páginas y me diera, al menos, una mínima información sobre el estado de las páginas que yo quiera.

Hay muchas opciones posibles. Por ejemplo podríamos usar un spider. Pero como ya escribí un artículo muy similar y me apetecía usar Ajax y Javascript, decidí utilizar el XMLHttpRequest.

Básicamente, la funcionalidad del script consiste en visitar las páginas que yo le configuro usando el XMLHttpRequest, y éste me dará el Status y el StatusCode de cada una de esas páginas.

Como ya sabréis, si la página funciona correctamente, devolverá un StatusCode "200" y un Status "OK". Si no existe la página devolverá un "404 - Page Not Found" y si hay un error devolverá "500 - Internal Server Error". Es poca información, pero nos viene perfecto para dar un repaso rápido a una gran cantidad de páginas.

Además de esta funcionalidad básica, el script "maquilla" los resultados devueltos, indicando, por ejemplo, cuando comienza y termina un test, o coloreando en rojo todo aquello que no sea un "200 - OK".

Lo he metido todo en un .ASCX en lugar de un .dll, porque a mí me suele ser más cómodo, sobretodo en cosas de administración tan pequeñitas.


Ahora vayamos al grano, mostrando un poquito del código básico:

El .ascx.cs tiene dos métodos centrales el "AddJavascriptCore" y el "AddJavascriptTest". Veamos el primero de ellos:


    private void AddJavascriptCore()
    {
        string js = string.Format(
            @"
    
        // Simplemente devuelve una variable "XMLHttpRequest" válida para IE y FF
        // It simply returns an XMLHttpRequest variable, valid for IE and FF
        function CreateXmlHttp()
        {{
            try
            {{
                xmlHttp = new ActiveXObject('Msxml2.XMLHTTP');
            }}
            catch(e)
            {{
                try
                {{
                    xmlHttp = new ActiveXObject('Microsoft.XMLHTTP');
                }}
                catch(oc)
                {{  
                    xmlHttp = null;
                }}
            }}
            if(!xmlHttp && typeof XMLHttpRequest != 'undefined')
            {{
                xmlHttp = new XMLHttpRequest();
            }}
            return xmlHttp;
        }}


        // Se llama al empezar el test, y muestra el mensaje de inicio
        // It's called at the beginning of the test and returns a start message
        function BeginTest()
        {{
            var panel = document.getElementById('{0}');
            panel.appendChild(document.createTextNode('BEGIN TEST'));
            panel.appendChild(document.createElement('br'));
        }}


        // Controlamos cuándo se ha llamado a todas las páginas
        // We control when are tested all the pages
        function SubstractCount()
        {{
            i--;
            if (i==0)
            {{
                var panel = document.getElementById('{0}');
                panel.appendChild(document.createTextNode('END TEST'));

            }}
        }}


        // Recoge la variable XMLHttpRequest y maneja la función que se ejecutará cuando la llamada termine.
        // Get the XMLHttpRequestVariable and hands the function that is going to execute when the call ends.
        function SendTest(ajaxRequest)
        {{
            var xmlHttp = CreateXmlHttp();
            xmlHttp.onreadystatechange = function() {{Test(ajaxRequest, xmlHttp)}};
            xmlHttp.open('GET', ajaxRequest, true);
            xmlHttp.send('');
        }}

        
        // Cuando termine la llamada, se crean los elementos DOM y se muestra el resultado
        // When the call ends, the DOM elements are created and the results are shown
        function Test(ajaxRequest, xmlHttp)
        {{
            if(xmlHttp.readyState == 4)
            {{
                var panel = document.getElementById('{0}');

                var item = document.createElement('div');

                var ajaxUrlRequest =document.createElement('a');
                ajaxUrlRequest.href= ajaxRequest;
                ajaxUrlRequest.target= '_blank';
                ajaxUrlRequest.appendChild(document.createTextNode(ajaxRequest));

                item.appendChild(ajaxUrlRequest);
                item.appendChild(document.createTextNode(': ' + xmlHttp.status + ' - ' + xmlHttp.statusText));

                var color = 'blue';

                if (xmlHttp.status != '200') color = 'red';
                    item.setAttribute('style', 'color:' + color);

                panel.appendChild(item);

                SubstractCount();
            }}
        }}
", pnlTesterResults.ClientID);

        // Registra el script. Se usa ASP.NET AJAX, pero en lugar de ésta puedes usar el ClientManager.
        // Registers the script. Here is used ASP.NET AJAX, but you can use the ClientManager instead
        ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "Subgurim_AjaxPageTester_AddJavascriptCore", js, true);
    }

    

En cuanto al método "AddJavascriptTest", es mucho más sencillo, pues sólo se encarga de inicializar variables y llamar al método para cada una de las páginas.

    private System.Collections.Specialized.StringCollection urlList = new System.Collections.Specialized.StringCollection();
    public System.Collections.Specialized.StringCollection UrlList
    {
        get { return urlList; }
        set { urlList = value; }
    }

    private void AddJavascriptTest()
    {
        StringBuilder sb = new StringBuilder();

        if (null != UrlList)
        {
            sb.AppendFormat("var i = {0};", UrlList.Count);
            sb.Append("BeginTest();");
            foreach (string url in UrlList)
            {
                sb.AppendFormat("SendTest('{0}');", url);
            }
        }

        ScriptManager.RegisterStartupScript(this, this.GetType(), "Subgurim_AjaxPageTester_AddJavascriptTest", sb.ToString(), true);
    }




Así pues, tras registrar el control en cualquier página .aspx, lo utilizaremos del siguiente modo:

        // Añade las url que quieras comprobar. Deben estar en el mismo dominio que esta página.
        // Add the URL that you want to test. They should be in the same domain that this page.
        AjaxPageTester1.UrlList.Add("/default.aspx");
        AjaxPageTester1.UrlList.Add("/login.aspx");



Añadir una cookie fácilmente con Javascript

(Ve aquí si lo que te interesa son las cookies en ASP.NET)

Hay muchas formas de escribir una cookie usando javascript. Yo he utilizado funciones que realmente consiguen lo que quieres, pero feas y complejas de leer.

El otro día me tope con una función extremadamente simple que cumple el objetivo que queremos el 95% de veces: una cookie con una clave, un valor y una fecha de caducidad.

function writeLightCookie(n, v, d)
{
    document.cookie = n + "=" + v + ";expires=" + d;
}


Donde "n" es el nombre o clave de la cookie, "v" es su valor, y "d" su fecha de caducidad.

Usarlo es también muy sencillo. Por ejemplo vamos a añadir una cookie que expire en un día:

var exp = new Date();
var expDays = 1;
exp.setTime(exp.getTime() + (expDays*24*60*60*1000));
writeLightCookie('nombreDeLaCookie', 'ValorDeLaCookie' , exp);



Regex Evaluator: un iGadget para expresiones regulares

Hace tiempo, las expresiones regulares me parecían infernales: ¡no había por donde cogerlas!

Como siempre suele pasarme en estos casos, no entenderlas hería mi honor , así que me puse manos a la obra... ¡ahora no puedo vivir sin ellas! Se han convertido en parte imprescindible, y las uso bastante, allá donde un "indexOf" o un "Replace" de un string normal se queda corto.

Ya he escrito sobre expresiones regulares en un par de artículos (introducción a expresiones regulares y Cómo trabajar las expresiones regulares en ASP.NET), además, uno de mis proyectos OpenSource (Improvement Kit), está basado justamente en su uso.

El asunto es que cuando trabajas con expresiones regulares, es bastante común tener que comprobar su correcto funcionamiento, porque a partir de cierta complejidad es prácticamente imposible que aciertes a la primera .

Hasta ahora yo trabajaba con el programa que indico en el artículo ya mencionado introducción a expresiones regulares (llamado Expresso). Es un programa muy potente y sencillo de usar, pero no es gratuito... además tampoco es un programa Web, algo hacia lo que tiende prácticamente todo.

Por tanto, dado que me estoy aficionando a hacer iGadgets por doquier, decidí hacer un iGadget de expresiones regulares. Para los que no sepáis lo que es un iGadget, se trata de los Gadgets que te puedes instalar en la página principal de Google... para los que no sepáis que es un Gadget (¡¡deberíais saberlo!!), son pequeñas aplicaciones que ofrecen pequeñas funcionalidad muy sencillas de usar.

Puedes instalártelo en tu página principal de Google desde aquí.






¿Qué estoy haciendo?

Como ya sabéis, a mí me gusta mucho probar todo lo que aparece en el mundo Web. Sobretodo las API's de las grandes como Google, Facebook, Delicious, Yahoo, etc.

Pues tenía yo un asuntito pendiente con Twitter, la cual, según mi teoría de dimesión temporal en Internet, ya es una Web centenaria.

Como ya sabréis Twitter es el paradigma del microblogging. Básicamente consiste en que te creas un usuario y escribes mensajitos de 140 caracteres como mínimo. Hay muchísimos modos de enviar los mensajes, desde la propia web (lo típico), desde un mensaje de móvil (lo que hace que puedas escribir lo que quieras desde donde quieras)... pero el que más me gusta, sin duda, es desde mensajería instantánea.

Yo por ejemplo uso GTalk, y publicar un mensaje no consiste más que en enviar un mensaje al usuario twitter (primero hay que activarlo, las instrucciones en la web están muy claras).

Vamos que ha sido probarlo y ya estoy viciado

Normalmente la gente lo usa para decir qué está haciendo en cada momento, o qué está pensando, o cualquier cosa que se te ocurra en menos de 140 caracteres. Yo no he podido resistir la tentación y he usado el Widget que ofrecen, el cual permite mostrar los últimos mensajes que has escrito en tu blog. Como veréis lo tengo a la izquierda bajo el título de "¿Qué estoy haciendo ahora?"

Estoy escribiendo en mi terrible inglés... la verdad es que no sé por qué... igual a partir de ahora escribo en castellano... igual mañana me aburro y no escribo nada...

Igual se me ocurre algún modo de utilizar Twitter para Todoexpertos.com... (esto es secreto, no se lo digáis a nadie).

La cosa es que la API de Twitter tiene muy buena pinta... aunque está terriblemente mal explicada

Me tengo que aguantar para no hacer una API en ASP.NET para usar la API de Twitter... no tengo tiempo para nada!!!!!



.NET 3.5 y Visual Studio 2008... ¡qué pronto!

Tal y como están los tiempos, hacerse eco en el mundo de Internet de una noticia que ya pasó hace dos días es poco menos que estar anticuado.

Pero bueno, lo tengo que decir: ya se ha publicado ASP.NET 3.5 y Visual Studio 2008.

Y sólo puedo decir una cosa al respecto... ¡¡qué pronto!! No sé por qué, pero ya saba por hecho que saldrían a la luz a finales de febrero... incluso ya me había planteado una estrategia de trabajo para los próximos meses en los que me dedicaba por completo a ASP.NET 3.5 a partir del mes de Febrero, para estar totalmente preparado cuando saliera...

Pero bueno, yo me alegro... ¡¡de hecho me encanta!! Me muero de ganas de hacer cosas con LINQ, trabajar con C# 3.0, utilizar los nuevos controles (ListView, Pager...), poder ver varias MasterPages anidadas en el modo diseño o debuggear javascript.

Mejoras las hay a cientos, aunque el cambio de ASP.NET 1.1 a ASP.NET 2.0 fue bastante más gordo. ScottGu, el mandamás de estas cosas de Microsoft, habla bastante al respecto del nuevo lanzamiento.

De momento ya he creado un apartado específico de ASP.NET 3.5 en mi blog, y me muero de ganas de escribir acerca de muchas cosas