Uso de controladores y módulos HTTP para crear componentes conectables ASP.NET
TOC
Collapse the table of content
Expand the table of content

Uso de controladores y módulos HTTP para crear componentes conectables ASP.NET

18 de Julio de 2005

Publicado: Septiembre de 2004

Scott Mitchell, 4GuysFromRolla.com

Atif Aziz, Skybow AG

Resumen: en este artículo, Scott Mitchell y Atif Aziz muestran cómo utilizar controladores y módulos HTTP para agregar registros de errores a las aplicaciones ASP.NET. (22 páginas impresas.) (Este artículo contiene vínculos a páginas en inglés.)

Descargar el archivo de ejemplo MSDNElmah.msi.

En esta página

Introducción Introducción
ELMAH: Controladores y módulos de registro de errores ELMAH: Controladores y módulos de registro de errores
Breve descripción general de controladores y módulos HTTP Breve descripción general de controladores y módulos HTTP
Análisis de la arquitectura de ELMAH Análisis de la arquitectura de ELMAH
Adición de ELMAH a una aplicación Web ASP.NET Adición de ELMAH a una aplicación Web ASP.NET
Conclusión Conclusión
Referencias Referencias
Bibliografía relacionada Bibliografía relacionada

Introducción

¿Ha trabajado alguna vez en una aplicación ASP.NET y diseñado algún conjunto de características o funcionalidad útil con el fin de volver a utilizarse con facilidad en otra aplicación ASP.NET? ASP.NET ofrece diferentes herramientas para dividir en varios componentes distintos tipos de funcionalidad. Las dos herramientas más comunes para su reutilización en ASP.NET son las siguientes:

  • Controles de usuario y controles de servidor compilados y personalizados para funcionalidad y elementos de la interfaz de usuario.

  • Biblioteca de clases de .NET para lógica empresarial y código de acceso a datos.

Dos herramientas de reutilización de ASP.NET a las que no se suele prestar demasiada atención son los controladores y módulos HTTP.

Si no está familiarizado con estos conceptos, no se preocupe. Este tema se tratará más adelante en el artículo. Por ahora, sólo es necesario comprender que los módulos HTTP son clases que se pueden configurar para ejecutarse como respuesta a eventos que se desencadenan durante la solicitud de un recurso ASP.NET. Un controlador HTTP es una clase responsable de procesar un recurso concreto o un tipo determinado de recurso. De hecho, cada vez que se agrega una página Web ASP.NET al proyecto, básicamente se está escribiendo un controlador HTTP. Esto se debe a que cuando la parte HTML de una página Web ASP.NET se compila dinámicamente en tiempo de ejecución, se hereda directa o indirectamente de System.Web.UI.Page, que resulta ser una implementación de controladores HTTP. Esto es cierto independientemente de si se adopta una estrategia de código subyacente o en línea.

Tal y como se conoce, una aplicación ASP.NET suele constar de un conjunto de páginas Web que se invocan cuando se solicitan mediante un explorador de usuario final. Para los desarrolladores de ASP.NET, la mayor parte del código escrito es específico a una solicitud de una página Web determinada como, por ejemplo, el código en una clase de código subyacente de una página concreta para mostrar los resultados de la base de datos en función de alguna consulta de búsqueda. No obstante, hay veces en las que es necesario escribir código que se asocie a una sola página Web, código que se aplique a todas las páginas de una aplicación. Por ejemplo, puede que se desee realizar un seguimiento del orden en el que cada usuario se desplaza por el sitio Web. Para ello, es necesario que en cada página se registre la hora de la solicitud y la información que identifica al usuario.

Un modo de proporcionar esta funcionalidad de registro puede ser agregar código que registre los datos relacionados en una base de datos en el controlador de eventos Page_Load para cada página Web del sitio. Sin embargo, este enfoque resulta difícil de mantener o reutilizar. Cada vez que se agrega una nueva página ASP.NET al sitio, es necesario asegurarse de que se incluye el código de registro adecuado. Si se quisiera agregar funcionalidad similar a otro sitio, habría que ir a través de cada página del sitio y agregar el código necesario. Lo ideal sería que la funcionalidad de registro estuviera separada lógica y físicamente de la funcionalidad de cada página independiente y que agregarla a otro sitio resultara tan sencillo como colocar un ensamblado en el directorio /bin del sitio.

Esta reutilización y mantenimiento es posible con los controladores y módulos HTTP. En este artículo se analizará un conjunto de controladores y módulos HTTP diseñados para hacer que el registro de errores resulte una tarea reutilizable y que se pueda mantener en gran medida. El objetivo consiste en demostrar el modo en que se pueden utilizar los módulos y controladores HTTP como una forma de alto nivel de modularidad, permitiendo el desarrollo, empaquetado e implementación de conjuntos completos de funcionalidades como una sola unidad independiente de las aplicaciones Web. En gran parte este objetivo se conseguirá a través de un análisis de una aplicación que se beneficie de la reutilización y modularidad mediante los módulos y controladores HTTP.

ELMAH: Controladores y módulos de registro de errores

Los controladores y módulos de registro de errores (Error Logging Modules And Handlers, ELMAH) que se analizarán en el artículo fueron escritos por Atif Aziz (http://www.raboof.com/) y resultaron ser una forma sencilla de agregar capacidades de registro de errores a una aplicación Web ASP.NET. ELMAH muestra cómo se pueden utilizar estos módulos y controladores para proporcionar un elevado nivel de modularidad para el código que se asocie a la aplicación Web (por ejemplo, registro de toda la aplicación). ELMAH es una solución fácilmente conectable; es decir, se puede agregar dinámicamente a una aplicación Web ASP.NET en ejecución sin necesidad de recompilación ni reimplementación.

No importa cómo se haya escrito o comprobado una aplicación Web determinada, la situación fracasará en diversas ocasiones. Puede que el error no esté en el código; puede ser que el servidor de correo electrónico no responda o que algún daño en los datos cause un error criptográfico. Independientemente del motivo, cuando se produce una excepción, especialmente en un sitio activo, es importante que se registren todos sus detalles para poder determinar el problema. ELMAH proporciona un mecanismo para la notificación y el registro de errores centralizado. Siempre que se produce una excepción que no se detecta en una aplicación ASP.NET, ELMAH recibe una notificación y controla la excepción tal y como se indica en el archivo Web.config. Esto puede incluir el registro de los detalles de la excepción en una base de datos, el envío de un correo electrónico a un administrador o ambas cosas.

ELMAH no está diseñado para responder correctamente a las excepciones no controladas. Simplemente registra los detalles de estas excepciones. Una vez que ELMAH se haya agregado a una aplicación Web ASP.NET, se registrarán todas las excepciones no controladas que surjan en esta aplicación. ELMAH no afecta a la experiencia de usuario final si se produce una excepción no controlada. Aún se podrá ver la página "Error del servidor", o bien, si se han configurado errores personalizados que controlen errores HTTP 500, al usuario se le redirigirá a una página con un mensaje más descriptivo. Pero mientras tanto, ELMAH habrá detectado que se ha producido una excepción no controlada y habrá registrado los detalles.

ELMAH detecta las excepciones no controladas mediante el evento Error del objeto HttpApplication. El evento Error se produce siempre que una excepción no detectada se hace notar durante el procesamiento de solicitudes, ya sea a partir de una biblioteca de clases .NET o una página Web ASP.NET. Se debe tener en cuenta que un gran número de aplicaciones ASP.NET implementan incorrectamente páginas de error personalizadas y realizan el control llamando al método Server.ClearError(). Con la eliminación del error se evitará que se desencadene el evento Error (así como que se indique al cliente) y ELMAH nunca podrá registrar la excepción. Para expresarlo de otro modo, al utilizar ClearError() en una página de error personalizada, el usuario verá que se ha producido un problema, pero usted no.

Nota

Para obtener más información sobre la creación de páginas de error personalizadas, consulte el artículo de Eli Robillard Rich Custom Error Handling with ASP.NET.

Figura 1. Visualización del registro de errores

Figura 2. Visualización de un error

Este registro también se puede procesar como RSS, permitiendo de este modo que un administrador reciba notificaciones a través de su agregador RSS favorito cuando se haya producido un error (consulte la figura 3).

Figura 3. Comentario RSS de errores

Nota

RSS (Really Simple Syndication) es un estándar con formato XML que se suele utilizar para distribuir noticias y otros tipos de contenido cambiante. Para obtener más información sobre RSS, incluyendo cómo distribuir contenido utilizando RSS y cómo crear un lector RSS basado en Web, consulte el artículo Creating an Online News Aggregator with ASP.NET.

Por motivos de brevedad, únicamente se tratará un subconjunto de características de ELMAH y los componentes clave. El código completo se puede descargar con este artículo y se recomienda un estudio a fondo del mismo para obtener los detalles de la implementación. Asimismo, existe una configuración del espacio de trabajo GotDotNet para ELMAH en http://workspaces.gotdotnet.com/elmah con el fin de poder analizar, informar de errores y estar al día de cualquier cambio.

Soluciones existentes para el registro de errores centralizado

Mientras que ASP.NET no ofrece capacidades integradas de visualización y registro de errores, el grupo Patterns & Practices de Microsoft ha creado un registrador de errores de código fuente abierto, Exception Management Application Block (EMAB). EMAB se diseñó para trabajar con aplicaciones de escritorio y .NET basadas en Web, aunque no se puede evitar considerar que se diseñó fundamentalmente para aplicaciones de escritorio con aplicaciones Web como idea de último momento, ya que EMAB publica detalles de las excepciones en el Registro de sucesos de Windows de forma predeterminada. Aunque el registro de eventos es un almacén de respaldo adecuado para la información breve de excepciones para una aplicación de escritorio, la mayor parte de las aplicaciones Web, especialmente las que se alojan en un servidor compartido de una compañía de hospedaje Web, evitan la utilización del registro ya que su uso requiere permisos especiales para establecerse para permitir que la aplicación ASP.NET escriba en el registro. Es cierto que EMAB es lo suficientemente flexible como para que se pueda crear un editor personalizado que registre información en una base de datos, pero eso es un paso adicional al que se enfrenta el desarrollador.

Nota

ELMAH dispone de un módulo de registro de bases de datos para Microsoft SQL Server 2000 que se analizará más adelante. Con ELMAH también es posible crear registradores de excepciones personalizados como, por ejemplo, uno que registre los detalles de excepciones en un archivo XML en el sistema de archivos del servidor Web. De hecho, ELMAH se puede ampliar para utilizar el bloque de aplicación de administración de excepciones, si ya se dispone de un editor personalizado escrito para el EMAB que se desea emplear.

El modo en que EMAB se utiliza para registrar información de excepciones influye en gran medida en la capacidad de mantenimiento y reutilización de la aplicación Web. Por ejemplo, un enfoque simplista a la hora de registrar información sería la colocación de un bloque try ...catch alrededor de cada bloque de código de cada una de las páginas Web ASP.NET, llamando a EMAB en la sección catch.

private void Page_Load(object sender, EventArgs e) 
{ 
  try { 
    // Code that might cause an exception 
  } 
  catch (Exception ex) { 
    // record exception information by calling exception logger library 
  } 
}

Este enfoque resulta imprudente, ya que asocia muy bien el registro de excepciones con cada una de las páginas Web ASP.NET, haciendo que resulte cualquier cosa menos reutilizable y con capacidad de mantenimiento. Una opción mejor sería utilizar EMAB en el evento Application_Error en Global.asax. Este enfoque ofrece una arquitectura con mayor capacidad de mantenimiento, reutilización y de acoplamiento flexible, ya que el código de publicación de la excepción no toca ninguna página Web ASP.NET y se sitúa en una ubicación centralizada. La desventaja es que no es conectable. Para agregar esta funcionalidad de registro de errores a otra aplicación Web ASP.NET, se debería modificar el elemento Global.asax de la aplicación, necesario para volver a compilar e implementar la aplicación.

El objetivo de este artículo no es introducir un sustituto para EMAB. Se pretende resaltar la modularidad o división de componentes que se hace posible mediante los módulos y controladores HTTP. ELMAH muestra cómo se puede adoptar una tarea común como, por ejemplo, el registro de errores centralizado, y dividirla en componentes para facilitar el mantenimiento y obtener un elevado nivel de reutilización. El propósito de ELMAH es servir de orientación para dividir en componentes la funcionalidad aplicable.

Breve descripción general de controladores y módulos HTTP

Antes de analizar los detalles de la implementación y arquitectura de ELMAH, se examinarán los módulos y controladores HTTP. Siempre que una solicitud llega a un servidor Web IIS, IIS examina su extensión para decidir cómo proceder. Para el contenido estático como, por ejemplo, páginas HTML, archivos CSS, imágenes o archivos JavaScript, IIS administra por sí mismo la solicitud. En el caso del contenido dinámico como páginas ASP, páginas Web ASP.NET y servicios Web ASP.NET, IIS delega la solicitud en una extensión ISAPI específica. Una extensión ISAPI es una parte de código no administrado que sabe cómo procesar solicitudes de un tipo concreto. Por ejemplo, la extensión ISAPI asp.dll es responsable de procesar solicitudes para páginas Web ASP clásicas; la extensión aspnet_isapi.dll se invoca cuando se recibe una solicitud para un recurso ASP.NET.

Además de las extensiones ISAPI, IIS también permite filtros ISAPI. Un filtro ISAPI es una parte de código no administrado que se puede ejecutar como respuesta a eventos generados por IIS. Durante el ciclo vital de una solicitud, IIS pasa a través de una serie de pasos que generan los eventos correspondientes. Por ejemplo, un evento se genera cuando la solicitud llega por primera vez a IIS, si se va autenticar y cuando el contenido procesado se va a volver a enviar al cliente, etc. Los filtros ISAPI suelen utilizarse para proporcionar capacidades como, por ejemplo, reescritura de direcciones URL, compresión, autorización y autenticación especializada, registro especializado, etc.

Cuando una solicitud para un recurso ASP.NET llega a IIS, se enruta al motor de ASP.NET, que procesa el contenido para el recurso solicitado. Este motor se comporta de forma muy similar a IIS, ya que genera una serie de eventos cuando la solicitud pasa a través de la canalización HTPP de ASP.NET. Asimismo, el motor de ASP.NET delega el procesamiento del recurso solicitado en una clase concreta. Siempre que IIS utiliza extensiones y filtros ISAPI no administrados, ASP.NET emplea clases administradas denominadas módulos y controladores HTTP.

Un controlador HTTP es una clase responsable de procesar un tipo determinado de recurso. Por ejemplo, la clase de código subyacente para una página Web ASP.NET es un controlador HTTP, que sabe cómo procesar el marcado para la página Web concreta. Esto ayuda a considerar a los controladores como procesadores especializados que saben cómo crear el marcado para un tipo determinado de recurso.

Nota

Para obtener información más completa sobre los controladores HTTP junto con algunas aplicaciones prácticas, consulte el artículo Serving Dynamic Content with HTTP Handlers.

Un módulo HTTP es una clase que puede incluirse en los distintos eventos generados cuando una solicitud pasa a través de las fases de su ciclo vital en el servidor. Un evento de este tipo de la aplicación ASP.NET es Error, desencadenado cuando se produce una excepción no controlada, que es el evento en el que ELMAH está interesado.

Nota

Para obtener más información sobre los módulos HTTP, incluyendo una explicación sobre cómo utilizarlos para implementar la reescritura de direcciones URL, consulte URL Rewriting in ASP.NET.

En la figura 4 se muestra una representación gráfica de la canalización HTTP de ASP.NET. Se debe tener en cuenta que el proceso comienza cuando una solicitud llega a IIS. Si se considera que el recurso solicitado se configura para ser administrado por la extensión ISAPI de ASP.NET, IIS envía la solicitud a la extensión ISAPI no administrada aspnet_isapi.dll. Esta extensión transmite la solicitud al motor de ASP.NET administrado. Durante el ciclo vital de la solicitud, puede que se ejecuten uno o varios módulos HTTP, dependiendo de qué módulos se hayan registrado y a qué eventos se hayan suscrito. Finalmente, el motor de ASP.NET determina el controlador HTTP que es responsable de procesar el contenido, invocando el controlador y devolviendo el contenido generado a IIS que, a su vez, lo devuelve al cliente solicitante.

Figura 4. Flujo de datos a través del registrador de errores

ELMAH ofrece un registro de errores centralizado a través de un módulo HTTP que dispone de un controlador de eventos para el evento Error. Cuando se desencadena el evento, ELMAH registra los detalles de la excepción. Asimismo, ELMAH emplea controladores HTTP que principalmente son responsables de generar formato HTML y RSS para mostrar la información del registro de errores.

La configuración de una aplicación Web existente para que utilice varios controladores o módulos se puede realizar copiando el ensamblado del controlador o módulo en el directorio /bin de la aplicación Web y unas cuantas líneas de configuración en el archivo Web.config.

Para configurar los módulos HTTP para una aplicación Web, se debe incluir una sección <httpModules> en el archivo Web.config que especifique el tipo de módulo que se vaya a agregar:

<httpModules> 
   <add name="ModuleName" type="ModuleType" /> 
</httpModules>

ModuleType es una cadena que indica el tipo de módulo, que es el nombre completo de la clase (Namespace.ClassName) seguido del nombre del ensamblado. El atributo type también puede incluir información sobre la versión y referencia cultural, junto con un símbolo de clave pública necesario para los ensamblados con nombre. En el siguiente fragmento de código se muestra la configuración real de <httpModules> que es necesario utilizar para incluir el módulo de registro de errores de ELMAH en la aplicación ASP.NET:

<httpModules> 
  <add name="ErrorLog" type="GotDotNet.Elmah.ErrorLogModule, 
    GotDotNet.Elmah, Version=1.0.5527.0, Culture=neutral, 
    PublicKeyToken=978d5e1bd64b33e5" /> 
</httpModules>

En una aplicación Web se puede utilizar un controlador HTTP agregando una sección <httpHandlers> al archivo Web.config. Debido a que un controlador HTTP procesa el contenido de un tipo determinado de recurso, además de un atributo type el elemento <httpHandlers> contiene un atributo path, que indica qué extensiones o rutas del archivo se deben asignar a este controlador HTTP. Existe también un atributo verb que permite limitar el uso del controlador a tipos específicos de solicitudes HTTP, como en una solicitud GET o POST. En el siguiente ejemplo se puede crear un controlador HTTP que se invoque para todas las solicitudes en los archivos con extensión .ashx.

<httpHandlers> 
   <add verb="*" path="*.ashx" type="HandlerType" /> 
</ httpHandlers >

El atributo type para el controlador HTTP se expresa utilizando las mismas opciones de sintaxis que con los módulos HTTP. Esta configuración en Web.config también se puede situar en el archivo machine.config, que habilita los módulos y controladores para todas las aplicaciones Web del servidor. En el siguiente fragmento de código se muestra el elemento <httpHandlers> del archivo Web.config de la demostración que se incluye en la descarga de este artículo. Se debe tener en cuenta que se indica que cualquier solicitud entrante para /elmah/default.aspx se debe procesar mediante la clase ErrorLogPageFactory.

<httpHandlers> 
    <add 
        verb="POST,GET,HEAD" 
        path="elmah/default.aspx" 
        type="GotDotNet.Elmah.ErrorLogPageFactory, 
          GotDotNet.Elmah, Version=1.0.5527.0, Culture=neutral, 
          PublicKeyToken=978d5e1bd64b33e5" /> 
</httpHandlers>

Tal y como se puede observar, la adición de controladores y módulos HTTP a una aplicación Web ASP.NET es bastante sencilla, se puede realizar en cuestión de segundos y no requiere ninguna recompilación o reimplementación de la aplicación ASP.NET. Por este motivo, los controladores y módulos HTTP son una estupenda herramienta para la reutilización y proporcionan una forma de dividir la aplicación en partes con gran capacidad de mantenimiento y de acoplamiento flexible.

Análisis de la arquitectura de ELMAH

La arquitectura de ELMAH consta de tres subsistemas:

  • Subsistema de registro de errores.

  • Subsistema de módulos HTTP.

  • Subsistema de controladores HTTP.

El subsistema de registro de errores es responsable de dos tareas: registrar errores en el registro y recuperar información de los mismos del registro. El subsistema de módulos HTTP se encarga de registrar un error cuando se produce una excepción no controlada en la aplicación ASP.NET. El subsistema de controladores HTTP proporciona un modo para que el registro de errores se procese en marcado, constituyendo una interfaz basada en el Web en el registro de errores, así como un comentario RSS.

Tal y como se muestra en la figura 5, los subsistemas de controladores y módulos HTTP utilizan el subsistema de registro de errores. El subsistema de módulos HTTP envía información de la excepción al subsistema de registro de errores, mientras que el subsistema de controladores HTTP lee y procesa la información del error.

Figura 5. Situación del sistema de registro de errores

Para comprender mejor la arquitectura de ELMAH, se examinará cada uno de los tres subsistemas con más detenimiento.

Subsistema de registro de errores

Es responsable de registrar los errores en el registro, así como de ofrecer capacidades para recuperar información sobre un error determinado o subconjunto de errores. Esta funcionalidad se encuentra disponible gracias a una serie de clases:

  • ErrorLog: esta clase abstracta proporciona métodos contractuales para leer y escribir en el registro.

  • Error: incluye propiedades que describen los detalles de un error determinado.

  • ErrorLogEntry: representa una instancia de Error concreta para una instancia de ErrorLog determinada. Principalmente ErrorLogEntry agrupa una instancia de Error con la instancia de ErrorLog a partir de la que se origina.

A continuación se analizarán estas tres clases y el modo en que funcionan con los subsistemas de controladores y módulos HTTP con el fin de ofrecer una utilidad completa de registro de excepciones centralizado.

Clase ErrorLog

Dependiendo de la estrategia o configuración concreta de un proyecto, puede que se desee emplear un almacén de respaldo diferente para el registro de errores. Por ejemplo, en un servidor de producción, se puede desear registrar las excepciones en Microsoft SQL Server, pero en un servidor de desarrollo los errores se pueden almacenar en un conjunto de archivos XML o una base de datos de Microsoft Access. Para ofrecer la capacidad de uso de distintos almacenes de respaldo, el subsistema de registro de errores proporciona una clase base abstracta, ErrorLog, que define los métodos base que todos los registradores de errores ELMAH deben implementar. Estos métodos son los siguientes:

  • Log(Error): registra un error en el almacén de respaldo. La clase Error representa información sobre una excepción no controlada; en breve se analizará esta clase con más detalle. Al registrar la información del error, el método Log() también debe asignar un identificador único al error.

  • GetError(id): devuelve información sobre un error concreto del registro.

  • GetErrors(...): devuelve un subconjunto de errores del registro. El subsistema de controladores HTTP emplea este método para mostrar el registro de errores de forma paginada, en lugar de mostrar todos los errores de una vez.

ELMAH incluye dos implementaciones de ErrorLog:

  • SqlErrorLog: registra errores en una base de datos de Microsoft SQL Server 2000 utilizando el proveedor System.Data.SqlClient. SqlErrorLog requiere SQL Server 2000, ya que aprovecha algunas de sus características XML, aunque este es un detalle de implementación que puede modificarse.

  • MemoryErrorLog: registra errores en la memoria de la aplicación (RAM). Es decir, se enlaza al dominio de aplicación de tal modo que cada aplicación recibe su propio registro privado. Es obvio que este registro no resiste los reinicios de la aplicación ni la duración, por lo que principalmente resulta adecuado para comprobaciones y soluciones de problemas temporales cuando otras implementaciones puedan presentar errores.

Se pueden utilizar cualquiera de estos registradores de excepciones agregando simplemente unas cuantas líneas de texto al archivo Web.config de la aplicación Web ASP.NET. Si es necesario almacenar los detalles de la excepción en algún lugar distinto de SQL Server o la memoria de la aplicación, se puede crear un registro personalizado propio. Para implementar un registrador de errores para ELMAH, se debe crear una clase que amplíe ErrorLog y proporcione la implementación para Log(), GetError() y GetErrors() en el almacén deseado.

Los subsistemas de controladores y módulos HTTP de ELMAH interactúan directamente con la clase ErrorLog especificada, ya sea SqlErrorLog, MemoryErrorLog o una clase del registro personalizado propio. El módulo HTTP registra la información de la excepción creando una instancia de Error y pasándola al método Log() de ErrorLog. Los controladores HTTP leen detalles sobre uno o varios errores a través de los métodos GetError() y GetErrors() de ErrorLog, que devuelven una instancia específica de ErrorLogEntry o un conjunto de instancias de ErrorLogEntry.

Clase Error

El método Log() de ErrorLog espera un parámetro de entrada de tipo Error. La clase personalizada Error se utiliza en lugar de la clase Exception proporcionada en .NET Framework, ya que Exception es más adecuada para comunicar la información de excepción a través de la pila de código y durante la duración de una aplicación. No obstante, los objetos Exception no son muy adecuados para almacenar un registro de excepciones debido a temas de portabilidad, escritura y almacenamiento. La serialización binaria se podría utilizar para almacenar una instancia de Exception, pero esto requeriría que el objeto Exception fuera deserializable en un equipo con el mismo conjunto de tipos y ensamblados disponibles. Se trata de una limitación inaceptable (especialmente desde el punto de vista de la administración y las operaciones), ya que un registro y su contenido deben ser transportables y no sólo visibles en un equipo con un tiempo de ejecución y configuración determinados. Asimismo, en ocasiones una instancia de Exception carece de información de margen específica a una aplicación Web como, por ejemplo, los valores de la colección ServerVariables de la actual solicitud Web, algo que puede ser inapreciable para el diagnóstico. En resumen, la clase Error actúa como suplente de todos los tipos de excepción, manteniendo la información de una excepción generada en una aplicación Web.

En la tabla 1 se incluye la lista completa de las propiedades de Error.

Propiedad

Descripción

Exception

Instancia de Exception representada por este error. Se trata de una propiedad en tiempo de ejecución que nunca persiste junto con una instancia de la clase.

ApplicationName

Nombre de la aplicación en la que se ha producido este error.

HostName

Nombre del equipo host en el que se ha producido este error. Un buen valor predeterminado es Environment.MachineName.

Type

Tipo, clase o categoría del error. Suele ser el nombre completo del tipo (sin la cualificación del ensamblador) de la excepción.

Source

Origen del error, generalmente el mismo que la propiedad Message de un objeto Exception.

Message

Texto breve que describe el error, generalmente el mismo que la propiedad Message de un objeto Exception.

Detail

Texto detallado del error como, por ejemplo, el seguimiento completo de la pila.

User

Usuario que inicia la sesión en la aplicación en el momento del error como, por ejemplo, el que devuelve Thread.CurrentPrincipal.Identity.Name.

Time

Fecha y hora en la que se ha producido este error. Siempre se expresa en hora local.

StatusCode

Código de estado que se devuelve en el encabezado de respuesta como resultado del error. Por ejemplo, 404 para FileNotFoundException. Lamentablemente este valor no se puede determinar siempre de forma confiable desde ASP.NET. Para algunos casos este valor StatusCode puede indicarse como cero.

WebHostHtmlMessage

Mensaje HTML predeterminado que el host de Web (ASP.NET) puede haber generado sin la presencia de páginas de error personalizadas.

ServerVariables

Elemento NameValueCollection de variables del servidor Web como, por ejemplo, las incluidas en HttpRequest.ServerVariables.

QueryString

Elemento NameValueCollection de variables de cadena de consulta HTTP como, por ejemplo, las que se incluyen en HttpRequest.QueryString.

Form

Elemento NameValueCollection de variables de formulario como, por ejemplo, las que se incluyen en HttpRequest.Form.

Cookies

Elemento NameValueCollection de cookies enviadas por el cliente; por ejemplo, las que incluye HttpRequest.Cookies.

Es necesario explicar la propiedad WebHostHtmlMessage. Si la aplicación Web ASP.NET se encuentra con una excepción no controlada y la aplicación no está configurada para utilizar páginas de error personalizadas, se verá una pantalla similar a la que aparece en la figura 6. Se trata de una pantalla que todos los desarrolladores de ASP.NET habrán visto en diversas ocasiones.

Figura 6. Página de error estándar

Cuando se genera una excepción, se tiene acceso al marcado HTML real para la pantalla correspondiente y se guarda en la propiedad WebHostHtmlMessage de la clase Error. Si se visita la página que muestra información detallada sobre una excepción concreta, si la instancia de Error correspondiente cuenta con un valor en su propiedad WebHostHtmlMessage, al visitante se le presenta un vínculo a una página que mostrará la pantalla de información de la excepción real (figura 6). No solamente se registra la excepción, sino que también se puede visitar la página del error original que genera ASP.NET al examinar el registro posteriormente. Y todo esto al mismo tiempo que se habilitan los errores personalizados.

La clase Error también cuenta con métodos para serializar y deserializar su estado a un formato XML y desde el mismo. Para obtener más información, consulte los elementos FromXml y ToXml del código.

Clase ErrorLogEntry: Asociación de Error con ErrorLog

La clase final del subsistema de registro de errores es ErrorLogEntry, que asocia una instancia de Error con otra de ErrorLog. Cuando el subsistema de controladores HTTP llama al método GetError() para recuperar información sobre una excepción determinada, dicho método recupera la información del almacenamiento de respaldo específico y la completa en una instancia de ErrorLogEntry. La clase ErrorLogEntry dispone de tres propiedades:

  • Id: identificador único de los detalles de la excepción.

  • Log: referencia a la instancia de ErrorLog que representa el almacenamiento de respaldo.

  • Error: instancia completa de la clase Error con los detalles del error específico.

Mientras que el método GetError() devuelve una sola instancia de ErrorLogEntry, GetErrors() devuelve una lista de instancias de ErrorLogEntry. GetErrors() se encuentra especialmente diseñado para permitir que los errores se paginen a través de n registros al mismo tiempo.

En la figura 7 aparece una vista actualizada de la arquitectura de ELMAH, en la que se muestra el subsistema de registro de errores.

Figura 7. Arquitectura actualizada

Subsistema de módulos HTTP

ELMAH consta de dos módulos HTTP: ErrorLogModule y ErrorMailModule. ErrorLogModule crea un controlador de eventos para el evento Error de la aplicación. En el caso de una excepción no controlada, el módulo HTTP obtiene el registrador de errores adecuado tal y como se especifica en la configuración de la aplicación y llama al método Log(), pasando una instancia de Error que se llena con la información de la excepción y HttpContext para la solicitud actual. En el siguiente código fuente se muestra el código relacionado de la clase ErrorLogModule:

public class ErrorLogModule : IHttpModule 
{ 
    public virtual void Init(HttpApplication application) 
    { 
        application.Error += new EventHandler(OnError); 
    } 
 
    protected virtual ErrorLog ErrorLog 
    { 
        get { return ErrorLog.Default; } 
    } 
 
    protected virtual void OnError(object sender, EventArgs args) 
    { 
        HttpApplication application = (HttpApplication) sender; 
        LogException(application.Server.GetLastError(), 
          application.Context); 
    } 
 
    protected virtual void LogException(Exception e, 
      HttpContext context) 
    { 
        try 
        { 
            this.ErrorLog.Log(new Error(e, context)); 
        } 
        catch (Exception localException) 
        { 
            Trace.WriteLine(localException); 
        } 
    } 
}

La ejecución de ErrorLogModule comienza en el método Init(), donde se indica al tiempo de ejecución de ASP.NET que el método OnError() se debe invocar siempre que se genere el evento Error. El método OnError() hace referencia al objeto HttpApplication y llama al método LogException(), transmitiendo los detalles de la última excepción, así como la instancia de HttpContext específica de la solicitud determinada. LogException() simplemente llama al método Log() adecuado de la clase ErrorLog, pasando una nueva instancia de Error. (El constructor de la instancia de Error adopta una instancia de Exception y HttpContext y llena las propiedades de forma correspondiente; para obtener más información, consulte el código fuente disponible en la descarga.)

ErrorLogModule contiene una propiedad ErrorLog de sólo lectura y devuelve la instancia de ErrorLog, devuelta por ErrorLog.Default. Default es una propiedad estática de tipo ErrorLog de la clase ErrorLog. Consulta la configuración de la aplicación Web para determinar qué clase se debe utilizar para el registro de excepciones: SqlErrorLog, MemoryErrorLog o una clase de registro de excepciones personalizada.

Nota

En la sección Adición de ELMAH a una aplicación Web ASP.NET se analizará cómo configurar una aplicación Web para que utilice un registrador de excepciones específico. Es tan sencillo como agregar un par de líneas a los archivos Web.config o machine.config.

El otro módulo HTTP del subsistema es la clase ErrorMailModule, que envía un correo electrónico a un administrador en caso de excepción. No se examinará esta parte de ELMAH, aunque se puede consultar cómo utilizar este módulo en los ejemplos de código que se encuentran disponibles en la descarga de este artículo.

Subsistema de controladores HTTP

Es necesario recordar que el objetivo de los controladores HTTP es procesar el contenido de un tipo determinado de recurso. Cuando se recibe una solicitud en la canalización HTTP de ASP.NET, el motor de ASP.NET examina la ruta solicitada y determina qué controlador se debe utilizar para administrar el recurso solicitado. Concretamente, puede que una aplicación ASP.NET esté configurada para administrar una ruta determinada mediante un controlador HTTP o un generador de controladores. Un generador de controladores HTTP es una clase que no es directamente responsable de procesar el contenido, sino de seleccionar y devolver una instancia del controlador. Esta instancia devuelta es la que debe procesar el recurso solicitado.

El subsistema de controladores HTPP de ELMAH incluye una serie de clases de controlador HTTP diseñadas para producir el marcado para mostrar los errores registrados, junto con una sola clase del generador de controladores. Esta clase, ErrorLogPageFactory, examina la parte PathInfo de la dirección URL solicitada para determinar qué controlador HTTP debe generar el resultado.

Nota

La parte PathInfo de una dirección URL es cualquier contenido adicional que siga al nombre del archivo y se encuentra disponible a través de la propiedad PathInfo del objeto Request. Por ejemplo, en la dirección URL http://www.example.com/someDir/somePage.aspx/somePath, somePath es la parte PathInfo de la dirección. Para obtener más información sobre la terminología empleada en las distintas partes de una dirección URL y las propiedades del objeto Request correspondientes, consulte la entrada de blog de Rick Strahl, Making Sense of ASP.NET Paths.

En el siguiente fragmento de código se muestra el código más interesante de la clase del generador de controladores HTTP ErrorLogPageFactory:

public class ErrorLogPageFactory : IHttpHandlerFactory 
{ 
    public virtual IHttpHandler GetHandler(HttpContext context, 
      string requestType, string url, string pathTranslated) 
    { 
        string resource = 
          context.Request.PathInfo.Length == 0 ? string.Empty : 
            context.Request.PathInfo.Substring(1); 
 
        switch (resource.ToLower(CultureInfo.InvariantCulture)) 
        { 
            case "detail" : 
                return new ErrorDetailPage(); 
 
            case "html" : 
                return new ErrorHtmlPage(); 
 
            case "rss" : 
                return new ErrorRssHandler(); 
 
            default : 
                return new ErrorLogPage(); 
        } 
    } 
}

Tal y como se puede observar, el método GetHandler() de la clase ErrorLogPageFactory devuelve una instancia del controlador HTTP en función del elemento PathInfo de la solicitud. Si PathInfo es rss, se devuelve una instancia del controlador ErrorRssHandler, que procesa el registro como un comentario RSS. Si PathInfo es detail, se devuelve una instancia del controlador ErrorDetailPage, que muestra información sobre una excepción determinada.

En la configuración de la aplicación Web ASP.NET, se debe especificar una ruta que se asigne al generador de controladores HTTP ErrorLogPageFactory como, por ejemplo, ErrorLog.aspx. Para ver un comentario RSS del registro de excepciones, visite: http://www.example.com/ErrorLog.aspx/rss.

Las distintas clases de controladores HTTP de ELMAH, ErrorDetailPage, ErrorHtmlPage, ErrorRssHandler, ErrorLogPage, etc, procesan marcado diferente. Por ejemplo, el controlador ErrorRssHandler recorre los últimos 15 errores y emite el marcado XML adecuado para presentar esta información en formato RSS. Todos los demás controladores HTTP se derivan, directa o indirectamente, de la clase System.Web.UI.Page (a partir de la que se derivan todas las clases de código subyacente ASP.NET). Estos controladores HTTP relacionados con páginas reemplazan a los métodos Render() y OnLoad() de la clase Page para crear una interfaz HTML que muestre una lista paginable de las excepciones registradas. Consulte las figuras 1, 2 y 3 para ver capturas de pantalla de estas páginas.

Nota

Aunque la clase Error guarda las colecciones ServerVariables, QueryString, Form y Cookie, únicamente la colección ServerVariables se muestra en los detalles de una excepción. Esto se debe a que los parámetros y cookies de QueryString se pueden visualizar mediante los parámetros QUERY_STRING y HTTP_COOKIE de la clase ServerVariable respectivamente. La colección Form se omite debido a que puede incluir potencialmente decenas de kilobytes de información de estado de vista que generalmente no sirve de mucho para la mayor parte del diagnóstico. Obviamente, si se opta por ello, los detalles del controlador HTTP se pueden modificar con facilidad para incluir esta información.

Una vez examinados los tres subsistemas, se analizará cómo incorporar ELMAH a una aplicación Web ASP.NET existente. Se debe destacar la facilidad de la operación de agregar ELMAH a cualquier sitio, una ventaja de la modularidad que permiten los módulos y controladores HTTP.

Adición de ELMAH a una aplicación Web ASP.NET

Esta operación es bastante sencilla e incluye dos pasos:

  • Adición del ensamblado de ELMAH a la aplicación Web.

  • Configuración de la aplicación Web para utilizar los controladores y módulos HTTP.

ELMAH se puede aplicar a una aplicación Web determinada de un servidor Web copiando el ensamblado en el directorio /bin de la aplicación y estableciendo la configuración de ELMAH mediante el archivo Web.config. Es más, ELMAH se puede configurar para que se aplique a todas las aplicaciones Web de un servidor agregando el ensamblado a la Caché de ensamblados global (GAC) del servidor y agregando los mismos valores de configuración en machine.config en lugar de Web.config.

En el archivo Web.config (o machine.config) será necesario incorporar la siguiente configuración:

  • Un elemento <sectionGroup> en <configSections> que defina un nuevo nombre de sección, <gotdotnet.elmah>, con una sección dentro denominada <errorLog>, que disponga de información sobre cómo registrar la información de excepciones.

  • Una sección <gotdotnet.elmah> con otra sección interior llamada <errorLog>, que incluya una referencia de tipos para el registrador de excepciones que se desee que utilice ELMAH, junto con cualquier configuración específica del registrador.

  • Una entrada en la sección <httpHandlers> que indique la ruta que, tras visitarse mediante un explorador, procese varias vistas en el registro de errores.

  • Otra entrada en la sección <httpModules> que agregue ErrorLogModule a la canalización HTTP de ASP.NET.

En el siguiente fragmento de código del archivo Web.config incluido en la descarga de este artículo se muestra cómo se pueden especificar estos cuatro valores de configuración:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
  <configSections> 
    <!-- Allows for a new section group to the Web.config --> 
    <sectionGroup name="gotdotnet.elmah"> 
      <!-- Indicates that inside the section group there will be an 
              errorLog section --> 
      <section name="errorLog" 
        type="System.Configuration.SingleTagSectionHandler, 
          System, Version=1.0.5000.0, Culture=neutral, 
          PublicKeyToken=b77a5c561934e089" /> 
    </sectionGroup> 
  </configSections> 
 
  <!-- This section group contains the type of the exception logger 
         to use (SqlErrorLog, MemoryErrorLog, or a custom logger). 
         It also contain properties pertinent to the exception logger 
         (connectionString, for the SqlErrorLog). --> 
  <gotdotnet.elmah> 
    <errorLog type="GotDotNet.Elmah.SqlErrorLog, 
      GotDotNet.Elmah, Version=1.0.5527.0, Culture=neutral, 
      PublicKeyToken=978d5e1bd64b33e5" 
      connectionString="...connection string..." /> 
  </gotdotnet.elmah> 
 
  <system.web> 
    <!-- Register that a request to aspnetham/errorlog.aspx should 
        be serviced by the ErrorLogPageFactory HTTP Handler factory --> 
    <httpHandlers> 
      <add verb="POST,GET,HEAD" path="elmah/default.aspx" 
        type="GotDotNet.Elmah.ErrorLogPageFactory, 
        Skybow.Samples.AspNetHam, Version=1.0.5527.0, 
        Culture=neutral, PublicKeyToken=978d5e1bd64b33e5" /> 
    </httpHandlers> 
 
    <!-- Adds the ErrorLogModule HTTP Module to the HTTP pipeline. --> 
    <httpModules> 
      <add name="ErrorLog" type="GotDotNet.Elmah.ErrorLogModule, 
         GotDotNet.Elmah, Version=1.0.5527.0, Culture=neutral, 
         PublicKeyToken=978d5e1bd64b33e5" /> 
    </httpModules> 
 
    ... 
  </system.web> 
</configuration>

El elemento <sectionGroup> de <configSections> indica que habrá un grupo de secciones adicional en el archivo de configuración <gotdotnet.elmah>. Asimismo, indica que dentro de esta sección personalizada, se encontrará una sección de <errorLog>. Dentro del elemento real <gotdotnet.elmah> existe otro elemento <errorLog> que especifica qué implementación del registro de errores se debe utilizar. ELMAH incluye dos implementaciones integradas, SqlErrorLog y MemoryErrorLog. Se puede especificar cuál de ellas utilizar, o bien, utilizar un registrador de excepciones personalizado que se pueda haber creado, en el elemento <errorLog>. <errorLog> también incluye la configuración específica de una implementación del registro de errores. Por ejemplo, al utilizar el elemento <errorLog> para indicar que se debe emplear SqlErrorLog, es necesario incluir una propiedad connectionString que indique el modo de conexión a la base de datos. En la descarga se incluye la secuencia de comandos SQL para crear la tabla adecuada y los procedimientos almacenados asociados.

Nota

Si se desea enviar un correo electrónico a un administrador en el caso de una excepción no controlada, es necesario agregar otro elemento <section> en <sectionGroup> que defina un nuevo elemento llamado <errorMail>. Asimismo, en el elemento real <gotdotnet.elmah> será necesario agregar otro elemento <errorMail>. Para obtener un ejemplo de esta sintaxis, consulte el archivo Web.config de la descarga.

La sección <httpHandlers> especifica que ErrorLogPageFactory (un generador de controladores HTTP) se debe utilizar para recuperar un controlador HTTP que procese el contenido para ver el registro de errores. El valor del atributo path indica la dirección URL relativa a la raíz virtual de la aplicación que se obtiene con la visualización del registro de errores. Esto se puede modificar en cualquier momento, pero es necesario asegurarse de que se trata de una dirección URL con una extensión que se administra mediante el motor de ASP.NET. Es decir, si se modifica la ruta a errors.log, IIS se debe configurar para que asigne solicitudes a errors.log en la extensión ISAPI de ASP.NET (aspnet_isapi.dll). Si se desea garantizar que únicamente los administradores puedan ver el registro, utilice las capacidades de autorización de dirección URL de ASP.NET para restringir el acceso a un usuario concreto o a un conjunto de usuarios o funciones. Por otra parte, si se desea deshabilitar por completo el acceso al registro basado en Web, simplemente no configure la sección <httpHandlers>.

La sección <httpModules> agrega el módulo HTTP ErrorLogModule a la canalización HTTP de ASP.NET. Asegúrese de que se incluye esta configuración de <httpModules>; de lo contrario, ELMAH no detectará el evento Error y, por lo tanto, no registrará ninguna excepción no controlada.

Tal y como se puede observar, la incorporación de ELMAH a una aplicación Web ASP.NET existente resulta un proceso bastante sencillo. La simple implementación y capacidad de reutilización de ELMAH se debe a que se divide en componentes utilizando controladores y módulos HTTP.

Conclusión

Con este artículo se ha pretendido mostrar que los módulos y controladores HTTP constituyen unas herramientas magníficas a la hora de dividir en componentes la funcionalidad asociada a una aplicación Web ASP.NET. Las tareas comunes como, por ejemplo, el registro centralizado de toda la aplicación o el control de solicitudes a través de la aplicación completa, se pueden dividir en componentes mediante los módulos y controladores. Mediante el ajuste de esta funcionalidad en un conjunto de componentes, es posible obtener las ventajas de la implementación y la capacidad de mantenimiento y reutilización sin que sea necesaria la migración, integración o recompilación del código existente y las aplicaciones.

Para demostrar la modularidad que se hace posible con los controladores y módulos HTTP, se ha examinado ELMAH, un registro de errores centralizado y una aplicación de correo. ELMAH emplea un módulo HTTP para detectar cualquier evento Error en toda la aplicación, que se desencadena como resultado de la presencia de una excepción no controlada. La excepción se registra en una base de datos de SQL Server, en memoria o en algún almacenamiento de respaldo. Asimismo, ELMAH puede enviar por correo electrónico el contenido de la excepción a uno o varios destinatarios como desarrolladores y personal de operaciones.

Además de un módulo HTTP, ELMAH incluye un conjunto de controladores HTTP y un generador de controladores de este tipo con el fin de facilitar la visualización del registro de errores a través de un medio basado en Web. Esto incluye no sólo una página Web tradicional, sino también un comentario RSS. ELMAH mantiene un componente discreto al disponer de la funcionalidad de visualización contenida en un controlador HTTP, en contraposición a la necesidad de que la aplicación Web incluya una página Web ASP.NET que muestre dicha información. Con el uso de controladores HTTP la implementación de ELMAH resulta un proceso sencillo que no requiere una recompilación de la aplicación Web ni la carga de una página Web ASP.NET en el servidor de producción.

ELMAH es sólo un ejemplo de la eficacia de la modularidad que ofrecen los módulos y controladores HTTP. Quizás existan otros procesos implementados que impliquen a toda la aplicación que puedan beneficiarse de la división en componentes con módulos y controladores.

Disfrute de la programación.

Agradecimientos

Antes de enviar este artículo al editor de MSDN se contó con la ayuda de una serie de voluntarios que realizaron labores de revisión del artículo y proporcionaron sus comentarios acerca del contenido, gramática y orientación. Entre los principales colaboradores del proceso de revisión del artículo se incluyen Milan Negovan, Carl Lambrecht, Dominique Kuster, Roman Mathis, Raffael Zaghet, Muhammad Abubakar y Patrick Schuler.

Referencias

Bibliografía relacionada

Atif Aziz cuenta con casi 13 años de experiencia en el desarrollo de soluciones de la plataforma Microsoft. Es uno de los principales asesores de Skybow AG; su objetivo fundamental consiste en ayudar a los clientes a comprender y crear soluciones en la plataforma de desarrollo .NET. Atif colabora con frecuencia con la comunidad de desarrolladores de Microsoft dando charlas en conferencias de Microsoft y otras organizaciones; asimismo escribe artículos para publicaciones técnicas. Es ponente de INETA y presidente del grupo de usuarios suizo .NET (dotMUGS). Puede ponerse en contacto con él en atif.aziz@skybow.com, o bien, a través de su sitio Web http://www.raboof.com/.

Scott Mitchell, autor de cinco libros sobre ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha trabajado con las tecnologías Web de Microsoft desde 1998. Trabaja como consultor, profesor y escritor independiente. Puede ponerse en contacto con él en mitchell@4guysfromrolla.com, o bien, mediante su blog, en http://scottonwriting.net/.

Mostrar:
© 2016 Microsoft