如何解决"'扔掉'在当地发现的例外情况"?

时间:2017-10-30 12:48:06

标签: javascript node.js rest exception-handling async-await

在处理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错误只是误导而应该被禁用?

7 个答案:

答案 0 :(得分:23)

如果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检查中,但我相信这会使我的代码更难以阅读并且难以维护。

相反,如上所述,我希望这是处理这种情况的方法。

答案 1 :(得分:3)

返回承诺拒绝,而不是在try块中引发错误

  try {
    const isAllowed = await checkIfIsAllowed(request);

    if (!isAllowed) {
      return Promise.reject(Error("You're not allowed to do that."));
    }

    const result = await doSomething(request);

    sendResult(result);
  } catch (error) {
    throw error;
  }

答案 2 :(得分:1)

我认为詹姆斯·索普的答案有一个缺点。它不是DRY,在两种情况下,当您调用sendError时都可以处理异常。假设我们有很多这样的代码行,其中包含可以引发Exception的逻辑。我认为这会更好。

这是我的解决方法

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 :(得分:1)

由于这不是一个阻塞错误,而只是一个IDE建议,因此应该从两个侧面查看问题。

第一方面是性能。如果这是一个瓶颈,并且有可能在编译时使用它,或者在转移到新的(尚未发布的)nodejs版本时使用,则重复的存在并不总是一个不好的解决方案。在这种情况下,IDE似乎很准确,并且这种设计在某些情况下可能导致优化效果不佳。

第二方面是代码设计。如果这样可以使代码更具可读性并简化其他开发人员的工作,请保留它。从这个角度来看,上面已经提出了解决方案。

答案 4 :(得分:1)

与詹姆斯·索普(James Thorpe)的看法相反,我稍微喜欢投掷的方式。我看不出任何有力的理由来尝试在try块中处理本地错误,而不是从调用堆栈中更深处冒出的错误有任何区别……只是抛出它们。我认为这是一致性的更好应用。

由于这种模式更加一致,因此当您希望将try块中的逻辑提取到可能在另一个模块/文件中的另一个函数时,它自然更适合进行重构。

// main.js
try {
  if (!data) throw Error('missing data')
} catch (error) {
  handleError(error)
}

// Refactor...

// validate.js
function checkData(data) {
  if (!data) throw Error('missing data')
}

// main.js
try {
  checkData(data)
} catch (error) {
  handleError(error)
}

如果不是将错误扔进try块中,而是在try块之外进行重构,则逻辑必须更改。

此外,处理错误的缺点是使您记住早点返回,以便在遇到错误后try块不会继续执行逻辑。这可能很容易忘记。

try {
  if (!data) {
    handleError(error)
    return // if you forget this, you might execute code you didn't mean to. this isn't a problem with throw.
  }
  // more logic down here
} catch (error) {
  handleError(error)
}

如果您担心哪种方法的性能更高,则不应该这样做。从技术上讲,处理错误的性能更高,但是两者之间的区别绝对是微不足道的。

请考虑一下WebStorm过于自以为是的可能性。 ESLint甚至没有规则。两种模式都是完全有效的。

答案 5 :(得分:0)

“为什么不将异常用作常规流控制?”这个问题有很好的答案。 here

之所以不会在本地引发异常,是因为您在本地知道如何处理这种情况,因此从定义上来说,这并不是例外。

@James Thorpe的answer对我来说不错,但是@matchish认为它违反了DRY。我说一般来说不是。 DRY代表“不要重复自己”,是由人们创造的:“每个知识在系统中必须具有单一,明确,权威的表示形式”。应用于编写软件代码时,它是关于不重复 complex 代码。

实际上,通过将重复的代码提取到函数中,然后从先前重复的位置调用该函数,可以说任何被认为违反DRY的代码都是“固定的”。解决多个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的proposed solutiondoSomethingOnAllowedRequest的合同从永远不会引发异常的事物更改为通常会引发异常的事物,这给所有调用者带来了异常处理的负担。通过使多个调用者重复相同的错误处理代码,这很可能会导致违反DRY,因此抽象上我不喜欢它。实际上,这取决于整体情况,例如那里有多少个呼叫者,而实际上他们对错误有相同的响应。

答案 6 :(得分:-3)

这可能会给你一些提示,也许这可能是原因(不确定是否相关)。 Catch statement does not catch thrown error

" 你的try catch块失败的原因是因为ajax请求是异步的。 try catch块将在Ajax调用之前执行并自行发送请求,但是返回结果时会抛出错误,在稍后的时间点。

执行try catch块时,没有错误。抛出错误时,没有try catch。如果你需要尝试catch for ajax请求,总是把ajax try catch块放在成功回调中,永远不要在它之外。"