在生产中如何处理HTTP错误?

时间:2019-06-26 12:41:14

标签: node.js express

我正在开发快速应用程序,在指定了所有路由和中间件之后,我在server.js的末尾有了这个

// Log errors
app.use(function (err, req, res, next) {
    logger.error(err.stack);

    if(process.env.NODE_ENV === 'production')
        return res.status(500).send('Something broke!');

    next(err);
});

// Start server
app.listen(port, () => {
    logger.info('Server is up on port ' + port);
});

这样做的目的是捕获生产中的所有错误,并避免将敏感数据泄露给客户端。

我的一个控制器中有以下代码:

const createHTTPError = require('http-errors')

async function(req, res, next) {
    try {
        invoice = await Invoice.create({
            // data
        });
    }catch (e) {
        if(e instanceof Sequelize.ValidationError){
             logger.error(e);
             return next(createHTTPError(400, 'Validation did not pass: ' + e.message));
        }
    }
}

问题是,当用next()对象调用http-errors时,它冒泡到我的全部错误处理程序,但是信息丢失了,并且其中的err对象是一个简单的{{1 }}具有以下参数的实例:

Error

错误代码号丢失。错误对象类型丢失了(好吧,名称转换为字符串)。

我该怎么办?如果删除所有内容,则可能会泄露一些敏感信息。谢谢

1 个答案:

答案 0 :(得分:0)

所以我最终得到了这段代码:

const HTTPErrors = require('http-errors');
const HTTPStatuses = require('statuses');

// ... set up express, middlewares, routes...

// Log errors
app.use(function (err, req, res, next) {

    let messageToSend;

    if(err instanceof HTTPErrors.HttpError){
        // handle http err
        messageToSend = {message: err.message};

        if(process.env.NODE_ENV === 'development')
            messageToSend.stack = err.stack;

        messageToSend.status = err.statusCode;
    }else{
        // log other than HTTP errors (these are created by me manually, so I can log them when thrown)
        logger.error(err.stack);
    }

    if(process.env.NODE_ENV === 'production' && !messageToSend){
        messageToSend = {message: 'Something broke', status: 500};
    }

    if(messageToSend) {

        let statusCode = parseInt(messageToSend.status,10);
        let statusName = HTTPStatuses[statusCode];

        res.status(statusCode);

        // respond with html page
        if (req.accepts('html')) {
            res.send('<html><head><title>'+statusCode+' '+statusName+'</title></head><body><h1>'+statusCode+' '+statusName+'</h1>'+messageToSend.message+'<br/><br/>'+(messageToSend.stack ? messageToSend.stack : '')+'</body></html>');
            return;
        }

        // respond with json
        if (req.accepts('json')) {
            let responseObject = { error: statusName, code: statusCode, message: messageToSend.message };

            if(messageToSend.stack)
                responseObject.stack = messageToSend.stack;

            res.send(responseObject);
            return;
        }

        // default to plain-text. send()
        res.type('txt').send(statusName+' '+messageToSend.message);
        return;
    }

    // if this is not HTTP error and we are not in production, let express handle it the default way
    next(err);
});

此解决方案:

  • 检测并显示来自http-errors模块的HTTP错误(带有用于开发而未用于生产的堆栈跟踪)
  • 对于任何其他错误,它取决于是生产中(然后引发一般的500 Server错误)还是开发中(默认情况下,让express处理该错误,这意味着使用堆栈跟踪将其打印出来)
  • 根据Accepts标头设置错误输出的格式(因此,如果应用需要JSON,则它将发送JSON)

我还在404 catchall中利用了这个新的catchall函数:

// DEFAULT CATCH
app.use(function(req, res, next){
    next(HTTPErrors(404));
});