Mi caso de uso es enviar un mensaje de error JSON personalizado, ya que estoy usando Express para alimentar mi API REST. Creo que este es un escenario bastante común, por lo que me enfocaré en eso en mi respuesta.
Version corta:
Manejo rápido de errores
Defina el middleware de manejo de errores como otro middleware, excepto con cuatro argumentos en lugar de tres, específicamente con la firma (err, req, res, next). ... Usted define el middleware de manejo de errores en último lugar, después de otras llamadas app.use () y rutas
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
});
Genere errores desde cualquier punto del código haciendo:
var JSONError = require('./JSONError');
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Versión larga
La forma canónica de lanzar errores es:
var err = new Error("Uh oh! Can't find something");
err.status = 404;
next(err)
De forma predeterminada, Express maneja esto empaquetándolo cuidadosamente como una Respuesta HTTP con el código 404, y el cuerpo consiste en la cadena del mensaje adjunta con un seguimiento de la pila.
Esto no funciona para mí cuando uso Express como servidor REST, por ejemplo. Querré que el error se devuelva como JSON, no como HTML. Definitivamente tampoco querré que mi seguimiento de pila se mueva a mi cliente.
Puedo enviar JSON como respuesta usando req.json()
, por ejemplo. algo como req.json({ status: 404, message: 'Uh oh! Can't find something'})
. Opcionalmente, puedo configurar el código de estado usando req.status()
. Combinando los dos:
req.status(404).json({ status: 404, message: 'Uh oh! Can't find something'});
Esto funciona a las mil maravillas. Dicho esto, me resulta bastante difícil de escribir cada vez que tengo un error, y el código ya no se auto-documenta como lo next(err)
era el nuestro . Parece demasiado similar a cómo se envía una respuesta JSON normal (es decir, válida). Además, cualquier error arrojado por el enfoque canónico aún resulta en una salida HTML.
Aquí es donde entra en juego el middleware de manejo de errores de Express. Como parte de mis rutas, defino:
app.use(function(err, req, res, next) {
console.log('Someone tried to throw an error response');
});
También hago una subclase de Error en una clase JSONError personalizada:
JSONError = function (status, message) {
Error.prototype.constructor.call(this, status + ': ' + message);
this.status = status;
this.message = message;
};
JSONError.prototype = Object.create(Error);
JSONError.prototype.constructor = JSONError;
Ahora, cuando quiero lanzar un error en el código, lo hago:
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Volviendo al middleware de manejo de errores personalizado, lo modifico a:
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
}
Subclasificar el error en JSONError es importante, ya que sospecho que Express instanceof Error
comprueba el primer parámetro que se pasa a next()
para determinar si se debe invocar un controlador normal o un controlador de errores. Puedo eliminar el instanceof JSONError
cheque y hacer modificaciones menores para asegurarme de que los errores inesperados (como un bloqueo) también devuelvan una respuesta JSON.