Server.GetLastError no funciona en IIS 7.0 o superior: cómo solucionarlo y un truco general para IIS 7.5

Si llevas unos cuantos años en esto del desarrollo Web seguro que tienes todavía aplicaciones por ahí escritas en ASP 3.0, también conocido como «ASP Clásico». Este precursor del actual ASP era estupendo y funciona de maravilla aún hoy en día. A pesar de todas las virguerías técnicas existentes en la actualidad (que me encantan) me confieso un enamorado de esa antigua plataforma.

El caso es que aún hoy en día, si tienes que montar una aplicación de ASP 3.0 incluso en un moderno Windows Server 2008 R2 con IIS 7.5, podrás hacerlo sin problemas y funcionará todo de maravilla. O casi…

El otro día tuvimos que montar una de nuestras aplicaciones «legacy» en este entorno precisamente y todo parecía ir de maravilla. El caso es que nosotros instrumentamos todas nuestras aplicaciones, incluso  las antiguas, para llevar un registro automático de todos los eventos de interés que se producen: avisos, advertencias, operaciones importantes sobre los datos… y por supuesto los errores no gestionados y por tanto inesperados. Se trata de una buena práctica que deberías seguir en cualquier aplicación, y más en una para Internet y de la cual algunos ya me habréis oído hablar en ponencias por ahí.

En ASP clásico la forma de gestionar los errores inesperados pasa necesariamente por personalizar la página de error para el estátus 500 del servidor Web con una página .asp propia. En ésta se obtiene información sobre el error producido usando el método Server.GetLastError que devuelve un objeto ASPError con todos los detalles.

La configuración en IIS 7.0 o IIS 7.5 se hace de la siguiente forma:

1.- En las propiedades del servidor virtual se va a la sección de páginas de error:

2.- Una vez dentro de ésta aparecen la lista de códigos de estátus HTTP estándar, como por ejemplo el 404 para páginas no encontradas (muy recomendable gestionarlo), el 302 (acceso denegado), o el que nos interesa: 50, error interno del servidor. Sustituimos la página por defecto de IIS para el error 500 y colocamos una página .asp propia. Utilizando el mencionado objeto ASPError podemos obtener información sobre errores que se produzcan, anotar información sobre lo que ha pasado con todo lujo de detalles (error, tipo, ubicación, página, usuario, tipo de navegador…) para luego poder hacer un diagnóstico y detectar problemas. También devolveremos una página más bonita y adecuada para los usuarios, lo cual es muy importante también, y además es una buena práctica de seguridad el no mostrar mensajes detallados de error al usuario final.

No funciona en IIS 7.x

La idea es estupenda y funciona muy bien si lo hacemos en IIS 5.0 o 6.0, ¡pero en IIS 7.x no funcionará!. Es decir, sí que se llamará a nuestra página personalizada, y ésta funcionará. El problema es que el método Server.GetLastError devolverá un objeto con todas sus propiedades vacías, por lo que sabremos que se produce un error pero no tendremos ni un sólo detalle del mismo 🙁

Se trata de un bug reconocido por Microsoft desde IIS 7.0, pero que no ha sido corregido en ninguno de los Service Pack ni tampoco en la versión R2 del sistema operativo (nueva versión 7.5 de IIS). Son los riesgos de usar una tecnología antigua para la que Microsoft no tiene la menor intención de seguir invirtiendo ni un segundo.

¿Cómo lo solucionamos?

Como casi siempre hay una vía de escape para solventar el problema. IIS 7.x dispone de un gestor global de códigos de estado HTTP que se usa cuando no hay uno específicamente designado en la configuración. Resulta que en este gestor global de errores el objeto ASPError sí que se rellena correctamente.

Por lo tanto lo que tenemos que hacer es eliminar el estátus HTTP 500 de la lista de errores gestionados por IIS, para que no tenga asignada ni siquiera la página por defecto. Si editamos la configuración general de este módulo de gestión global de errores:

Nos aparece un diálogo en el que podremos ajustar nuestra página de gestión global:

Allí deberemos introducir una ruta relativa a la raíz de nuestra aplicación (por ejemplo «/Logevents/logerr.asp» como en la figura), y usar en el PathType la opción de «Ejecutar una URL».

De este modo se recibirán perfectamente las propiedades del error y podremos trabajar como siempre 🙂

¡Uppps! IIS me da un error de «violación de bloqueo» (Lock Violation)

Vaya. Cuando ya pensábamos que lo teníamos controlado y podríamos tener nuestra aplicación ASP Clásico funcionando a pleno rendimiento, al pulsar «OK» en el diálogo de la figura anterior nos sale este mensaje:

¿Qué demonios es esto? La verdad es que el mensaje no proporciona demasiada información al respecto. He de confesar que cuando nos salio la primera vez pensé que era un bug de la interfaz de gestión de IIS 7.5, que tenía interbloqueos mal hechos ;-P

A base de indagar y probar, ya que no había información en Internet al respecto y la poca que hay no está bien, llegué a la solución del problema.

Por defecto, hay ciertas secciones de la configuración global de IIS 7.x que están protegidas para impedir la modifiación en sub-ramas del servidor. Esto está diseñado de esta forma pensando en las empresas de hosting, ya que de esta manera pueden bloquear ciertas secciones peligrosas para que los administradores de los sitios web que venden no puedan cambiar ajustes que puedan comprometer la seguridad global del servidor. ¡Bien hecho Microsoft!. Lo malo es que no está nada claro en ningún sitio (o al menos a mi eso me parece), y a más de uno que gestiona sus propios servidores le puede traer por la calle de la amargura. ¡Mal hecho Microsoft! 😉

En nuestro caso lo que debemos hacer es desbloquear este ajuste en la configuración global del servidor para que nos permita cambiarlo en la configuración secundaria de los sitios Web.

Para ello debes ir, como administrador, a la carpeta «C:WindowsSystem32Inetsrv» o equivalente (está ahí incluso en versiones de 64 bits de Windows). Dentro de ésta encontrarás el archivo applicationHost.config. Sácale una copia por si acaso te cargas algo que no debes, y ábrelo con el bloc de notas, ya que es un simple archivo de texto XML.

Una vez abierto busca el nodo «<httpErrors>». Encontrarás una rama que pondrá algo similar a esto:

<httpErrors defaultPath=»» defaultResponseMode=»ExecuteURL»
lockAttributes=»allowAbsolutePathsWhenDelegated,defaultPath»>

La parte importante aquí es el atributo lockAttributes. Como puedes observar, por defecto, tiene bloqueada la modificación de la ruta por defecto para los errores, que es precisamente lo que pretendemos modificar en nuestro sitio web.

Elimina la palabra «defaultPath» del atributo lockAttributes, graba el archivo y ciérralo. Ahora vuelve a IIS y cambia d enuevo la configuración en el diálgoo que hemos visto. Esta vez, al cerrarlo, no te generará la violación de bloqueo, y todos felices.

Este último consejo te valdrá para IIS en general, y por lo tanto para ASP.NET o incluso PHP, y no se restringe sólo a esta sección de la configuración.

Espero que si llegas hasta aquí a través de un búsqueda, desesperado/a por el problema, todo esto te haya servido. Si me lo quieres agradecer puedes dejar un comentario, y también ya sabes 😉

Sin categoría

6 thoughts on “Server.GetLastError no funciona en IIS 7.0 o superior: cómo solucionarlo y un truco general para IIS 7.5

  1. Interesante artículo, gracias !!! Una pena que falte documentación para casos así, y una alegría que ustedes puedan aportar soluciones.

    No tengo ninguna aplicación asp 3.0, pero es bueno saber esta situaciones, nunca se sabe si puede caer algún mantenimiento de aplicaciones «clásicas».

    cuál es la mejor forma de instrumentar las aplicaciones ??

    salu2&grz

  2. Hola preguntoncojonero 😉

    La mejor forma de instrumentar en ASP.NET es usando el framework de Health Monitoring incluido en la plataforma, tema del que ya he escrito algo en el blog:

    · http://www.jasoft.org/blog/PermaLink,guid,1d75f7c9-7acd-4e0f-be49-14c70dcab853.aspx
    · http://www.jasoft.org/blog/PermaLink,guid,b3d0fa01-1352-4a99-b8b9-0c51d3cd2f30.aspx

    y del que he dado también algunas ponencias ya.
    En nuestro curso de preparación de la certificación desarrollo Web (70-562) en campusMVP (http://www.campusmvp.com) lo tengo muy desarrollado.

    Saludos

    JM

  3. Hola, he visto el video del CodeCamp sobre Health Monitoring, muy interesante si, y me surgieron algunas dudas, que hago aquí partícipe:

    – Los errores de Ajax en las aplicaciones Web se puede monitorizar de alguna manera, al igual que errores de javascript como comentaba en el vídeo con el OnError. Algún ejemplo ?

    – Qué incidencia en el rendimiento tiene activar el Health Monitoring?? Tenemos servicios WCF alojados en IIS y queremos monitorizarlos; con Health Monitoring parece ser algo potente, pero no queremos una merma en el rendimiento, a ver si alguien puede comentar algo al respecto.

    – Se puede habilitar y deshabilitar Health Monitoring fácilmente con enable=»off» en el web.config para la sección de healh monitoring, no?

    – Ojalá para Windows (si tuviéramos servicios wCF alojados en Servicios Windows) hubiera algo tan potente y fácil de configurar 🙂

    saludos y gracias !!!

  4. Hola preguntoncojonero:

    Este no es el mejor sitio para preguntar esto, pues trompel a temática del post.
    No obstante te diré que:

    1.- AJAX es JavaScript por lo que puedes usar un sistema similar (o modificar unpoco el que propongo) para detectarlos también e instrumentarlos.

    2.- El Health Monitoring puede impactar si activas muchos eventos que se lanzan continuamente. Además, la gestión que hagas de ellos puede tener incidencia puesto que si escribes a una BD, envías un mail, etc.. añades operaciones de E/S. En cualquier caso no deberías activar ningún evento que se esté lanzando todo el rato, y aunque lo hicieses, en ese caso deberías activar los modos de buffering que ofrece la infraestructura para que esas operaciones de E/S impacten poco.

    3.- Sí, tienes razón, s epuede activar y desactivar fácilmente con ese ajuste en el web.config. También puedes deshabilitar las reglas y listo.

    Un saludo,

    JM

  5. Que tal, tengo un problema en el trabajo, al instalar mis aplicaciones en el servidor (windows server 2008 r2 enterprise), con una base de datos (sql 2008), tienen que ser perfectas, ya que si tiene un error en el código el servicio de IIS se cae y se debe reiniciar, alguien puede ayudarme?

    Gracias

  6. Thank you a lot. I’ve been searching for a solution for this problem
    for hours. Your post really saved my day. It worked after I rebooted
    the server. I really grateful for your help.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *