如何在生产中处理 express 中的 http 错误?

mic*_*vka 6 node.js express

我正在开发 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);
});
Run Code Online (Sandbox Code Playgroud)

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

我的其中一个控制器中有此代码:

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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是,当next()http-errorsobject调用时,它冒泡到我的 catch-all 错误处理程序,但信息丢失了,其中 err 对象是一个Error带有这些参数的简单实例:

message = "Validation did not pass: notNull Violation: invoice.clientEmail cannot be null"
name = "BadRequestError"
stack = "BadRequestError: Validation did not pass: notNull Violation: invoice.clientEmail cannot be null\n    at module.exports (/home/XXXX/create-new-invoice.js:109:33)"
Run Code Online (Sandbox Code Playgroud)

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

我该怎么办?如果我删除我的全部内容,我就有可能泄露一些敏感信息。谢谢

mic*_*vka 6

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

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);
});
Run Code Online (Sandbox Code Playgroud)

这个解决方案:

  • 检测并显示http-errors模块中的 HTTP 错误(对于开发使用堆栈跟踪,对于生产不使用堆栈跟踪)
  • 对于任何其他错误,这取决于是在生产中(然后抛出通用 500 服务器错误)还是在开发中(让express默认处理错误,这意味着使用堆栈跟踪将其打印出来)
  • 根据 Accepts 标头格式化错误输出(因此,如果应用程序需要 JSON,则会发送 JSON)

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

// DEFAULT CATCH
app.use(function(req, res, next){
    next(HTTPErrors(404));
});
Run Code Online (Sandbox Code Playgroud)