Photo by Nathan Dumlao on Unsplash

Esperando lo Inesperado — Buenas prácticas para el manejo de errores en Angular

Michael Karén

--

“Esperar lo inesperado muestra un intelecto moderno y exhaustivo.” — Oscar Wilde

En este artículo escribiré acerca de cómo centralizar el manejo de errores en Angular. Cubriré algunos de los temas más comunes como:

  • errores en el lado del cliente
  • errores de parte del servidor
  • notificaciones del usuario
  • rastreo de errores

Durante la explicación presentaré ejemplos de códigos y al final proporcionaré un enlace a un ejemplo completo.

💂 English version:

¿A quién debemos culpar por los errores?

¿Por qué surgen errores en nuestras aplicaciones? ¿Por qué no se puede escribir un código en base a especificaciones que siempre funcione?

En última instancia, somos los seres humanos quienes creamos los programas y estamos propensos a cometer errores. Algunas razones detrás de los errores pueden ser:

  1. La complejidad de la aplicación
  2. La comunicación entre las partes interesadas
  3. Errores de parte del desarrollador de software
  4. La presión del tiempo
  5. La carencia de testing

Como ven, esta lista podría alargarse mucho; y teniendo esto en mente es lógico pensar que llegará el momento en que lo inesperado suceda y un error aparezca.

https://xkcd.com/1024/

¡Atrápalos si puedes!

Para atrapar excepciones sincrónicas en el código podemos agregar un bloque try/catch. Catch significa atrapar en inglés. Si un error es arrojado dentro de try entonces hay que agregar un catch y corregirlo. Si no se hace esto, la ejecución del script se detendrá.

Hay que tener en mente que esta acción se volverá insostenible rápidamente. No podemos agregar un catch para atrapar un error en todas las líneas del código. Entonces, es necesario un manejo de errores global.

Photo by Wil Stewart on Unsplash

¡Atrápalos todos!

Afortunadamente Angular provee un anzuelo para centralizar el manejo de errores con ErrorHandler.

La implementación por defecto deErrorHandler imprime mensajes de errores a la consola.

Podemos modificar esta acción creando una clase que implemente ErrorHandler:

Después podemos agregarlo en nuestro módulo raíz para cambiar la acción por defecto en nuestra aplicación. En lugar de usar la clase por defecto ErrorHandler vamos a usar nuestra propia clase:

Entonces ahora solo queda un lugar donde cambiar el código para el manejo de errores.

Photo by NeONBRAND on Unsplash

Errores de lado del cliente

Por el lado del cliente, cuando lo inesperado sucede, un error de JavaScript es arrojado. Esto trae consigo dos propiedades importantes que podemos usar.

  1. Mensaje — una descripción del error que puede ser leída por el humano.
  2. Stack o pila — Error Stack, o pila de errores, rastrea el historial de los archivos ‘responsables’ que causaron el Error.

Comúnmente, la propiedad mensaje es lo que le aparece al usuario si no escribimos nuestros propios mensajes de error.

Photo by imgix on Unsplash

Errores del lado del servidor

Por el lado del servidor, cuando algo sale mal, un HttpErrorResponse es arrojado. Este contiene una propiedad mensaje que podemos usar para notificaciones.

También arroja un código del estado del error. Este puede ser de diferentes tipos. Si empieza con un cuatro (4xx), entonces el cliente hizo algo inesperado. Por ejemplo, si recibimos un estado 400 (BadRequest), entonces la petición que el cliente envió no es lo que el servidor esperaba.

Los estados que empiezan con cinco (5xx) son errores del servidor. El más común es el de 500 Internal Server Error, un estado HTTP muy genérico que significa que algo erróneo sucedió en el servidor, pero el servidor no puede ser más específico en cuanto a lo que el problema puede ser.

Con diferentes tipos de errores, es de gran ayuda tener un servicio que procese los mensajes y sus trazas en la pila.

Like this blog post? Share it on Twitter! 🐦

Error service

Este servicio contiene la lógica para procesar los mensajes de error y sus trazas en la pila tanto del servidor como del cliente. El siguiente ejemplo es muy simple. Para casos más avanzados podríamos usar algo como stacktrace.js.

La lógica de este servicio se basa en la clase de errores que recibimos de nuestro servidor. También depende del tipo de mensaje que queremos mostrar a nuestros usuarios.

Comúnmente, no mostramos la traza de pila a nuestros usuarios. Sin embargo, si no estamos en un entorno de producción, si quisiéramos considerar mostrar la traza de pila a los probadores. En este escenario podemos usar una bandera que muestra la traza de pila.

HttpInterceptor

HttpInterceptor fue introducido en Angular 4.3.1 para proveer una manera de interceptar peticiones o respuestas HTTP y transformarlas o manejarlas antes de continuar con el procesamiento.

Existen dos casos que podemos implementar en el Interceptor.

Primero, podemos reintentar la llamada HTTP una o varias veces antes de arrojar un error. En algunos casos, por ejemplo, si recibimos un timeout, podemos continuar sin arrojar la excepción.

Para hacer esto posible usamos el operador retry de RxJS para volver a subscribirnos al observable.

Ejemplos más avanzados de este tipo de comportamiento:

Luego podemos chequear si el estado de la excepción y observar si es un 401 error no autorizado. Con una segurida basada en Tokens, podemos intentar refrescar el Token. Si esto no funciona, podemos redirigir al usuario a la a la página de autenticación.

Aquí hemos vuelto a intentar antes de observar el estado de error y arrojar de nuevo el error. Actualizar los Tokens de seguridad no forma parte del contexto de este artículo.

También es necesario proveer el interceptor que hemos creado.

Notificaciones

Para notificaciones uso Angular Material Snackbar.

Este nos proporciona notificaciones simples para el usuario cuando los errores ocurren.

Podemos manejar errores de servidor y de usuario de manera diferente; y, en vez de notificaciones, mostramos una página de error.

Mensaje de Error

Los mensajes de error tienen significado y es por ello que se de deben tomar en cuenta para ayudar al usuario a navegar. Al mostrar “Un error ha ocurrido” no le proveemos al usuario ni la causa del problema, ni como resolverlo.

En comparación, si mostramos algo como por ejemplo “Lo siento, tus fondos se han agotado”, entonces el usuario sabe cuál es la causa del problema. Un poco mejor, pero aún así esto no ayuda a resolver el problema.

Una mejor solución sería darle al usuario un menaje para pedirle que transfieran más dinero o proporcionar un enlace a la página donde puede hacerlo.

Recuerda que el manejo de error no es para sustituir un mal UX.

Con esto quiero decir que no debemos recibir errores que podamos anticipar. ¡Si una acción del usuario puede producir un error, entonces hay que arreglarlo!

No permitas que un error suceda solo porque creaste un buen mensaje para él.

Registro de errores

Si no registramos los errores, entonces el usuario que los encuentra será el único o única que sabrá que están ahí. Guardar la información es necesario para resolver un problema después.

Guardar la información requiere elegir la manera de cómo guardarla. Más acerca de éste tema después.

Cyanide & Happiness

¿Dónde debemos guardar la información?

Con un manejo de errores centralizado no nos lamentaremos por dejar esta decisión para después. Ahora sólo queda un lugar donde debemos cambiar nuestro código. Por ahora, registremos el mensaje en la consola.

Rastreo de errores

Idealmente, tú quieres identificar los errores en tu aplicación web antes de que los usuarios los encuentren. Registro de errores es el proceso para identificar proactivamente estas cuestiones y así arreglarlas lo más pronto posible.

No podemos esperar sentados a que los usuarios nos reporten los errores. En vez de esto debemos ser más proactivos y registrar y monitorear los errores.

Debemos tener conocimiento de los errores cuando éstos sucedan.

Podríamos crear nuestra solución para este propósito. Sin embargo, para qué reinventar la maquinaria cuando existen varios servicios excelentes como Bugsnag, Sentry, TrackJs, y Rollbar quienes se especializan en esta área.

Al usar una de estas soluciones para el rastreo de errores en el interfaz del usuario nos brinda la oportunidad de grabar y volver a ver las sesiones del usuario para ver, de primera mano, exactamente la experiencia del usuario.

Si no puedes reproducir un error, entonces no puedes arreglarlo.

En otras palabras, una solución para el rastreo de errores bien hecha puede alertarnos cuando un error suceda y ofrecernos percepción en cuanto a cómo replicar/resolver el error.

En un artículo anterior, Cómo enviar errores a Slack en Angular escribí acerca de Slack como herramienta para el rastreo de errores. El siguiente puede servir de ejemplo:

La implementación de una solución con más alcance no esta considerada en este artículo.

Todos juntos ahora

Como el manejo de errores es esencial, es lo primero que se carga. Por esta razón, no podemos usar inyección de dependencias en el constructor para los servicios. En vez, tenemos que inyectarlos manualmente con Injector.

Conclusión

El manejo de errores es un concepto básico para las aplicaciones empresariales. En este artículo hemos discutido la centralización del manejo de errores al sobreescribir el funcionamiento por defecto del ErrorHandler. También hemos agregado otros servicios adicionales.

  1. Un servicio de error para analizar mensajes y las trazas de pila.
  2. Un servicio para notificar a los usuarios acerca de los errores.
  3. Un servicio para registrar los errores.

También hemos implementado una clase Interceptor para:

  1. Reintentar antes de arrojar un error.
  2. El chequeo de errores específicos y respuesta en acorde.

Con esta solución, ya puedes rastrear tus errores y ofrecerles a tus usuarios una mejor experiencia.

Ejemplo de código en GitHub. 📜
Corre el código en StackBlitz. 🏃

Gracias a Vero Karén y Nacho Vazquez Calleja por contribuir en la traducción de este artículo. 🙏

Comencé con una frase de Oscar Wilde, por tanto terminemos con otra.

Picture from AZ Quotes

Call to Action

I always enjoy feedback so please 👏, 📝 and tweet 🐦.
Follow me on Twitter and Medium for blog updates.

--

--

Michael Karén
Michael Karén

Written by Michael Karén

Frontend Architect • JavaScript Enthusiast • Educative.io Author • ngVikings organizer.

Responses (2)