Как обрабатывать http-ошибки в экспрессе на производстве? - PullRequest
0 голосов
/ 26 июня 2019

Я занимаюсь разработкой экспресс-приложения, и после того, как я укажу все свои маршруты и промежуточное ПО, у меня это будет в конце 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 является простым 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)"

Код ошибки потерян. Тип объекта ошибки потерян (ну, преобразован в строку в имени).

Что мне делать? Если я уберу свою ловушку, я рискую получить утечку конфиденциальной информации. Спасибо

1 Ответ

0 голосов
/ 26 июня 2019

Итак, я получил этот код:

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 из модуля http-errors (с трассировкой стека для разработки и без для производства)
  • для любых других ошибок это зависит от того, находится ли в работе (затем сгенерировать общую ошибку 500 Server) или в разработке (пусть по умолчанию express обрабатывает ошибку, что означает распечатку с трассировкой стека)
  • форматирует вывод ошибок на основе заголовка Accepts (поэтому, если приложение ожидает JSON, оно отправляет JSON)

Я также пользуюсь этой новой функцией catchall в 404 catchall:

// DEFAULT CATCH
app.use(function(req, res, next){
    next(HTTPErrors(404));
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...