Meu caso de uso está enviando uma mensagem de erro JSON customizada, já que estou usando o expresso para alimentar minha API REST. Acho que esse é um cenário bastante comum, então vou me concentrar nele em minha resposta.
Versão curta:
Tratamento expresso de erros
Defina o middleware de tratamento de erros como outro middleware, exceto com quatro argumentos em vez de três, especificamente com a assinatura (err, req, res, next). ... Você define o middleware de tratamento de erros por último, depois de outro app.use () e encaminha chamadas
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);
}
});
Eleve os erros de qualquer ponto do código, fazendo:
var JSONError = require('./JSONError');
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Versão longa
A maneira canônica de lançar erros é:
var err = new Error("Uh oh! Can't find something");
err.status = 404;
next(err)
Por padrão, o Express lida com isso empacotando-o ordenadamente como uma resposta HTTP com o código 404 e o corpo consistindo na string de mensagem anexada a um rastreamento de pilha.
Isso não funciona para mim quando estou usando o Express como um servidor REST, por exemplo. Desejo que o erro seja enviado de volta como JSON, não como HTML. Eu também definitivamente não quero que meu rastreamento de pilha seja transferido para meu cliente.
Posso enviar JSON como resposta usando req.json()
, por exemplo. algo parecido req.json({ status: 404, message: 'Uh oh! Can't find something'})
. Opcionalmente, posso definir o código de status usando req.status()
. Combinando os dois:
req.status(404).json({ status: 404, message: 'Uh oh! Can't find something'});
Isso funciona como um encanto. Dito isso, acho bastante difícil digitar sempre que ocorre um erro, e o código não é mais autodocumentado como o nosso next(err)
. Parece muito semelhante a como uma resposta JSON normal (ou seja, válida) é enviada. Além disso, quaisquer erros lançados pela abordagem canônica ainda resultam em saída HTML.
É aqui que entra o middleware de tratamento de erros do Express. Como parte de minhas rotas, eu defino:
app.use(function(err, req, res, next) {
console.log('Someone tried to throw an error response');
});
Eu também faço a subclasse de Error em uma classe 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;
Agora, quando quero lançar um erro no código, eu faço:
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Voltando ao middleware de tratamento de erros personalizado, eu o modifico para:
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);
}
}
Subclassificar o erro em JSONError é importante, pois suspeito que o Express faz uma instanceof Error
verificação no primeiro parâmetro passado para a next()
para determinar se um manipulador normal ou um manipulador de erro deve ser invocado. Posso remover a instanceof JSONError
verificação e fazer pequenas modificações para garantir que erros inesperados (como uma falha) também retornem uma resposta JSON.