Manejador de archivos .js y .css para compresión y caché

 En el último artículo que escribí hablé sobre la compresión de páginas web, pero… ¿qué pasa si queremos comprimir otro tipo de archivos, como por ejemplo .css o .js? Al no tratarse de extensiones que el ISAPI de .net entienda, IIS (Internet Information Server) los leerá del disco y los servirá tal y como son.

 A continuación os propongo una solución para este problema, que nos permitirá aplicar compresión e incluso cachear estos y otros tipos de archivo. Se trata de los HttpHandlers de ASP.Net. Es un tema que ya se ha visto en otros artículos, pero merece la pena repasarlo para comprobar que su potencial es enorme.

 

Pasos a seguir

 Para crear nuestros propios manejadores http, tendremos que hacer lo siguiente:

  • Crear una clase que implemente la interfaz IHttpHandler.
  • Agregar el manejador a la configuración de nuestra aplicación en el web.config.
  • Configurar el servidor IIS para que las extensiones del manejador, se procesen a través del ISAPI de .Net.

 

Clase HeadFilesHandler

 La clase se llamará HeadFilesHandler, ya que se utilizará para manejar las extensiones de archivos que se incluyen en el head de la página (.css y .js). Esta clase implementa la interfaz IHttpHandler, que nos obliga a definir la propiedad IsReusable y el método ProcessRequest. A continuación se muestra el código de la misma:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.IO;

using System.Web.Caching;

using System.Text;

 

namespace DevJoker.Handlers

{

 

/// <summary>

/// Manejador de css y js que añade cach y compresión

/// </summary>

public class HeadFilesHandler : IHttpHandler

{

 

#region Constantes

 

const string PREF_CLAVE_CACHE = "Cache.HeadFiles";

const double TIEMPO_CACHE = 60;

const string ERROR_404 = "El archivo o la ruta especificada no existe";

const string ERROR_500 = "Error en el servidor";

 

#endregion

 

 

#region IHttpHandler Members

 

public bool IsReusable

{

get { return true; }

}

 

public void ProcessRequest(HttpContext context)

{

try

{

//Agregamos un filtro de compresión a la respuesta

ComprimirPagina();

 

//Obtenemos la ruta y la clave para la caché

string rutaArchivo = context.Request.PhysicalPath.ToLower();

 

//Se establece el tipo de respuesta dependiendo de la página

context.Response.ContentType = GetTipoMime(rutaArchivo);

string claveCache = String.Format("{0}.{1}",

PREF_CLAVE_CACHE, rutaArchivo);

 

//Comprobamos si ya está en caché

if (context.Cache[claveCache] != null)

{

context.Response.Write(context.Cache[claveCache].ToString());

}

else if (File.Exists(rutaArchivo))

{

//Leemos el archivo

string doc = File.ReadAllText(rutaArchivo,

Encoding.GetEncoding("iso-8859-1"));

 

//Guardamos en caché con el tiempo establecido

context.Cache.Add(

claveCache,

doc,

new CacheDependency(rutaArchivo),

DateTime.Now.AddMinutes(TIEMPO_CACHE),

Cache.NoSlidingExpiration,

CacheItemPriority.NotRemovable,

null);

 

//Escribimos el archivo en la respuesta

context.Response.Write(doc);

}

else

{

//Establece el tipo de respuesta como html

context.Response.ContentType = "text/html";

 

//Si no está en caché ni existe el archivo retornamos

//un 404 (Archivo no encontrado)

context.Response.StatusCode = 404;

context.Response.StatusDescription = ERROR_404;

context.Response.Write(String.Format(@" <html>

<head>

<title>{0}</title>

<head>

<body>

{0}:<br><br>{1}

</body>

</html>", ERROR_404, rutaArchivo));

}

}

catch (Exception ex)

{

//Establece el tipo de respuesta como html

context.Response.ContentType = "text/html";

 

//Si se produce un error retornamos un 500

//(Error interno del servidor)

context.Response.StatusCode = 500;

context.Response.StatusDescription = ERROR_500;

context.Response.Write(String.Format(@" <html>

<head>

<title>{0}</title>

<head>

<body>

{0}:<br><br>{1}<br><br>{2}

</body>

</html>", ERROR_500, ex.Message, ex.StackTrace));

}

}

 

/// <summary>

/// Devuelve el tipo mime del archivo indicado

/// </summary>

/// <param name="rutaArchivo">Ruta o nombre del archivo</param>

/// <returns>Tipo mime del archio indicado</returns>

private string GetTipoMime(string rutaArchivo)

{

string respuesta = String.Empty;

string extension =

rutaArchivo.ToLower().Substring(rutaArchivo.LastIndexOf("."));

switch (extension)

{

case "js":

respuesta = "application/x-javascript";

break;

case "css":

respuesta = "text/css";

break;

}

return respuesta;

}

 

/// <summary>

/// Comprime la página si el navegador lo soporta

/// </summary>

private void ComprimirPagina()

{

//TODO: Ver artículo "Compresion por gzip y deflate"

}

 

#endregion

 

}

 

}

 

Configuración del web.config

 Utilizaremos el mismo manejador para los dos tipos de archivo, así que tendremos que agregar las siguientes líneas en el web.config:

<httpHandlers>

<add verb="GET" path="*.js"

type="DevJoker.Handlers.HeadFilesHandler" validate="false"/>

<add verb="GET" path="*.css"

type="DevJoker.Handlers.HeadFilesHandler" validate="false"/>

</httpHandlers>

 Estos son los parámetros utilizados y su significado:

  • verb: Indica el tipo de petición http. En nuestro caso solo utilizaremos GET.
  • path: Indica el nombre de archivo o extensión que manejará el Httphandler.
  • type: Es la clase del manejador.
  • validate: Es un parámetro opcional que si se establede a false aumenta el rendimiento al inicio de la aplicación, ya que no instanciará la clase hasta que reciba una petición del tipo de archivo indicado en path.

 

Configuración del IIS

 Por último tendremos que configurar el servidor web para que las extensiones .js y .css se procesen a través del ISAPI de .Net. Esto lo haremos de la siguiente manera:

  1. Accedemos a las propiedades del directorio virtual dónde está la aplicación:

    [Ampliar Imagen]

  2. Pulsamos el botón configuración y nos sale la siguiente pantalla:

    [Ampliar Imagen]

  3. Agregamos las extensiones .js y .css y las configuramos para que utilicen el isapi de .net (c:\windows\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll):

    [Ampliar Imagen]

 

Conclusión

Para comprobar que va bien, se puede utilizar un sniffer como Http Analyzer. Veremos que se aplica compresión y se reduce el tiempo de carga de los archivos .js y .css. 

Gracias a los HttpHandlers personalizados podemos crear manejadores para cualquier tipo de archivo. Por ejemplo se pueden utilizar para validar descargas de documentos, como hace SharePoint.

 

Manejador de archivos .js y .css para compresión y caché
David Andres .

David es analista y desarrollador de aplicaciones web cliente-servidor especializado en ASP.Net. En sus años de experiencia ha participado en proyectos para importantes empresas y entidades como Panda Security, Merck Sharp And Dohm, el gobierno de Navarra o Repsol. Conoce a fondo todas las versiones del framework .Net, bases de datos SQL Server y Oracle, y otras tecnologías como MS SharePoint y W4 Workflow. En desarrollo domina los lenguajes C#.Net, VB.Net, Java (J2EE, Struts), C, C++, JavaScript, T-SQL y PL/SQL entre otros.
Fecha de alta:26/03/2009
Última actualizacion:26/03/2009
Visitas totales:3763
Valorar el contenido:
Últimas consultas realizadas en los foros
Últimas preguntas sin contestar en los foros de devjoker.com