This article has been localized into Spanish by the community.
Manejando Excepciones
Un error dentro de una porción de software puede ser muchas cosas, pero en lenguajes de programación más recientes, generalmente se les denomina excepciones. Una excepción es a menudo descrita como "un evento dentro de la ejecución de un programa, que interrumpe el flujo normal" - en otras palabras, algo que no se esperaba y debiera ser atendido. Esto es llamado manejo de excepciones y es una parte muy importante dentro del software moderno, incluyendo aplicaciones web.
En el mundo de la web sin embargo, con un framework como "ASP.NET Core", existen en realidad mas de un tipo de error con los que se debe lidiar. Hay excepciones, de las que hablaremos en este artículo, y luego están los errores que heredamos de la tecnología web/HTTP. Por ejemplo, si el usuario trata de acceder a una página que no existe, el error 404 es devuelto al visitante. Muchos servidores web tendrán una página por defecto para comunicar este error al usuario, de no ser así, el navegador se encargará de ello. Sin embargo, el error 404 es el mejor ejemplo de un error que no es realmente una excepción, y que desearas manejar de forma elegante en tu aplicación.
Para el usuario/visitante, no obstante, no hara demasiada diferencia que tipo de error o excepción se encuentre - lo único que importa es como lo manejas y te recuperas de ello, y de como se le presenta al usuario. Así, en este artículo, hablaremos de las excepciones y luego discutiremos los errores basados en HTTP en el próximo artículo.
Manejo de Excepciones
No entraré mucho en detalles sobre las excepciones en general, ya que esto es más relevante en un Tutorial del C#, pero toquemos el tema brevemente. Primero que todo, si ya sabes que una excepción puede ocurrir, p.ej. porque estas invocando métodos que pueden arrojar una excepción, por supuesto deberías manejarlos localmente, con un bloque try...catch:
public IActionResult Details(int id)
{
try
{
var product = DbHelper.GetProduct(id);
return View(product);
}
catch(Exception ex)
{
// Here you may want to log the exception and return an actual error page
return Content("Sorry, an error occurred!");
}
}
De todos modos, muchas excepciones pueden ser difíciles de anticipar, así que, si no quieres envolver todo tu código dentro de bloques try...catch, necesitarás manejar las excepciones a un nivel superior. Intenta abrir el archivo Startup.cs que se encuentra en tu proyecto MVC de "ASP.NET Core" y hecha una mirada al método Configure(). Como se vea y lo que haga dependerá de la plantilla que hayas usado para crear tu proyecto, pero si usaste una plantilla vacía y no la has cambiado, probablemente encontrarás unas pocas líneas de código, como estas:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if(env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
....
Básicamente le dicen a tu aplicación web que use la página especial de excepciones para desarrolladores, mientras la aplicación esté corriendo en modo desarrollo (lo veremos mas adelante). Verás el resultado cuando una excepción sea arrojada, ya sea porque se usó la palabra clave throw o porque se ha hecho algo que resultó en una excepción:
public IActionResult Index()
{
string s = null;
return Content(s.Trim());
}
Esto, obviamente, resultará en NullReferenceException - solo trata de acceder la acción en tu navegador y fíjate en el resultado. Dependiendo de la version de "ASP.NET Core" que uses, se verá mas o menos como esto:
Como puedes ver, ASP.NET te entrega una página de error muy detallada - con esta, deberías poder identificar el problema (incluso cuando no se tan obvio como en nuestro ejemplo) y arreglarlo. Por razones de seguridad, esta cantidad de detalles solo debería ser mostrado al o los desarrolladores y NUNCA a un usuario/visitante. En lugar de esto, cuando no estamos en modo de desarrollo, deberíamos mostrar un mensaje de error más amistoso al usuario, probablemente con una disculpa, y luego registrar la excepción p.ej. en una base de datos.
La primera parte, en donde mostramos algo amistoso al usuario, es fácil. Puede ser hecho de muchas formas, pero todo empieza con el código del que hablamos antes en el método Configure() del archivo Startup.cs. Necesitamos modificarlo para que maneje la situación en que la aplicación no está en modo de desarrollo, como esto:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if(env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
....
Con la ayuda del método UseExceptionHandler(), podemos redireccionar al usuario por un camino específico cuando un error se presente. En este caso, lo enviamos a "/Error", entonces necesitaremos un método para manejar esta ruta. Una manera de abordar esto sería definir una acción en HomeController llanada Error() o crear un ErrorController y definir un método Index(). Yo he usado la ultima propuesta y queda como sigue:
public class ErrorController : Controller
{
public IActionResult Index()
{
return Content("We're so sorry, but an error just occurred! We'll try to get it fixed ASAP!");
}
}
En una aplicación en el mundo real, esto por su puesto debería ser reemplazado con una bonita "View", la cual pueda incluso usar los mismos diseños que el resto de la página!
Detalle de Excepciones
Ahora podemos manejar una excepción inesperada de forma mas elegante, pero aún no sabemos que ocurrió. Ya que no mostramos ningún detalle al usuario, necesitamos manejarlo antes de hacerle saber que algo salió mal. Para acceder a la información sobre la excepción, podemos usar la interface IExeptionHandlerPathFeature. Se puede encontrar en el "namespace" Microsoft.AspNetCore.Diagnostics, así que necesitarás importar esto donde manejes la excepción, p.ej. en ErrorController:
using Microsoft.AspNetCore.Diagnostics;
Ahora podemos cambiar el método Index() para que pueda manejar la excepción, antes de notificárselo al usuario.
public class ErrorController : Controller
{
public IActionResult Index()
{
var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if((exceptionHandlerPathFeature != null) && (exceptionHandlerPathFeature.Error != null))
{
// In our example, the ExceptionHelper.LogException() method will take care of
// logging the exception to the database and perhaps even alerting the webmaster
// Make sure that this method doesn't throw any exceptions or you might end
// in an endless loop!
ExceptionHelper.LogException(exceptionHandlerPathFeature.Error);
}
return Content("We're so sorry, but an error just occurred! It has been logged and we'll try to get it fixed ASAP!");
}
}
Hecho esto, tu aplicación es ahora más robusta y podrá anticipar las excepciones que pudieran ocurrir.
Sumario
En este artículo, hemos aprendido a lidiar con excepciones, ya sean anticipables o no. Este es un paso importante para crear una aplicación web robusta y siempre deberías contar con una estrategia para esto antes que tu aplicación web sea liberada al público.
Otra cosa que deberías considerar es como manejar los distintos códigos de estado HTTP que tu aplicación devolverá al usuario, p.ej. el código 404 cuanto una página no es encontrada - discutiremos esto en el próximo artículo.