Как исправить "выброс" исключения, обнаруженного локально "?

В этой функции, которая обрабатывает вызов REST API, любая из вызываемых функций для обработки частей запроса может выдать ошибку, чтобы сигнализировать, что код ошибки должен быть отправлен как ответ. Однако сама функция также может обнаружить ошибку, после чего она должна перейти в блок обработки исключений.

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            throw new ForbiddenException("You're not allowed to do that.");
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

Webstorm будет подчеркивать throw следующим сообщением: 'throw' of exception caught locally. This inspection reports any instances of JavaScript throw statements whose exceptions are always caught by containing try statements. Using throw statements as a "goto" to change the local flow of control is likely to be confusing.

Однако я не уверен, как реорганизовать код, чтобы улучшить ситуацию.

Я мог бы скопировать код из блока catch в проверку if, но я считаю, что это сделает мой код менее читабельным и сложнее поддерживать.

Я мог бы написать новую функцию, которая проверяет isAllowed и выдает исключение, если это не удастся, но похоже, что это оборачивает проблему, а не исправляет проблему дизайна, которую предположительно сообщает Webstorm.

Неправильно ли мы используем исключения и почему мы сталкиваемся с этой проблемой или ошибка Webstorm просто ошибочна и должна быть отключена?

Ответ 1

Вы проверяете что-то и бросаете исключение, если isAllowed терпит неудачу, но вы знаете, что делать в этой ситуации - вызовите sendErrorCode. Вы должны бросать исключения внешним абонентам, если вы не знаете, как обращаться с ситуацией - то есть в исключительных обстоятельствах.

В этом случае у вас уже есть определенный процесс, что делать, если это произойдет - просто используйте его напрямую без косвенного throw/catch:

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            sendErrorCode("You're not allowed to do that.");
            return;
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

Я мог бы скопировать код из блока catch в проверку if, но я считаю, что это сделает мой код менее читабельным и сложнее поддерживать.

Наоборот, как и выше, я ожидал бы, что это будет способ справиться с этой ситуацией.

Ответ 2

Ответ Джеймса Торпа имеет один недостаток на мой взгляд. Это не DRY, в обоих случаях, когда вы вызываете sendError, вы обрабатываете исключения. Давайте представим, что у нас есть много строк кода с такой логикой, где может быть выдано исключение. Я думаю, что это может быть лучше.

Это мое решение

async function doSomethingOnAllowedRequest(req) {
    let isAllowed = await checkIfIsAllowed(req);
    if (!isAllowed) {
       throw new ForbiddenException("You're not allowed to do that.");
    }
    doSomething(req);
}
static async handleRequest(req) {
    try {
        let result = await doSomethingOnAllowedRequest(req);
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

Ответ 3

Есть хорошие ответы на вопрос "Почему бы не использовать исключения в качестве нормального управления потоком?" здесь

Причина, по которой вы не генерируете исключение, которое вы будете ловить локально, заключается в том, что вы локально знаете, как справиться с этой ситуацией, так что это, по определению, не исключение.

@James Торп ответ выглядит хорошо для меня, но @matchish чувствует, что это нарушает СУХОЙ. Я говорю, что в целом это не так. "СУХОЙ", что означает "Не повторяйся сам", определяется людьми, которые придумали фразу: "Каждая часть знаний должна иметь одно, однозначное, авторитетное представление в системе". Применительно к написанию программного кода речь идет о неповторении сложного кода.

Практически любой код, который, как говорят, нарушает DRY, называется "исправленным", извлекая повторяющийся код в функцию, а затем вызывая эту функцию из тех мест, где он был ранее повторен. Наличие нескольких частей вашего вызова кода sendErrorCode является решением для устранения проблемы с СУХОЙ. Все знания о том, что делать с ошибкой, находятся в одном определенном месте, а именно функции sendErrorCode.

Я бы немного изменил ответ @James Thorpe, но это скорее обман, чем реальная критика, заключающаяся в том, что sendErrorCode должен получать объекты или строки исключений, но не оба:

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            sendErrorCode(new ForbiddenException("You're not allowed to do that."));
            return;
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

Более серьезный вопрос - какова вероятность ошибки и целесообразно ли рассматривать !isAllowed в качестве исключения. Исключения предназначены для обработки необычных или непредсказуемых ситуаций. Я ожидаю, что !isAllowed будет обычным явлением, которое должно обрабатываться с помощью логики, специфичной для этой ситуации, в отличие, скажем, от внезапной неспособности запросить базу данных, которая имеет ответ на вопрос isAllowed.

Предлагаемое решение @matchish изменяет контракт doSomethingOnAllowedRequest с того, что никогда не вызовет исключения, на то, что обычно вызывает исключение, возлагая бремя обработки исключений на всех его вызывающих. Это может привести к нарушению DRY, поскольку у нескольких вызывающих абонентов повторяется один и тот же код обработки ошибок, поэтому в абстрактном виде это мне не нравится. На практике это будет зависеть от общей ситуации, например, от того, сколько абонентов там и действительно ли они одинаково реагируют на ошибки.

Ответ 4

Поскольку это не ошибка блокировки, а только рекомендация IDE, вопрос следует рассматривать с двух сторон.

Первая сторона - производительность. Если это узкое место и потенциально возможно использовать его при компиляции или при переносе на новые (еще не выпущенные) версии nodejs, наличие повторений не всегда является плохим решением. Кажется, что IDE намекает именно в этом случае, и что такой дизайн может привести к плохой оптимизации в некоторых случаях.

Вторая сторона - это дизайн кода. Если это сделает код более читабельным и упростит работу для других разработчиков - сохраните его. С этой точки зрения решения уже были предложены выше.

Ответ 5

Это может дать вам несколько советов, возможно, это может быть причиной (не уверен, если это необходимо). Операция Catch не выдает заброшенной ошибки

" Причина, по которой ваш блок catch try не работает, заключается в том, что запрос ajax является асинхронным. Блок catch try будет выполняться перед вызовом Ajax и отправляет сам запрос, но ошибка возникает, когда возвращается результат, AT LATER POINT IN TIME.

Когда выполняется блок catch try, ошибки не возникает. Когда ошибка возникает, нет попытки улова. Если вам нужно попробовать catch для ajax-запросов, всегда ставьте ajax try catch блоки внутри обратного вызова успеха, НИКОГДА вне его. "