【问题标题】:Java Spring Error Handling Performance in Web ApplicationsWeb 应用程序中的 Java Spring 错误处理性能
【发布时间】:2020-03-11 05:49:52
【问题描述】:

这专注于后端 Web 应用程序开发。我更喜欢在我的应用程序中使用映射到我的业务逻辑的 HTTP 状态代码。例如,如果发出请求但未找到用户,我将抛出异常并使用@ControllerAdvice@ExceptionHandler 处理它并映射到未找到的 404。有时我在 try catch 块中有这些用于记录并重新抛出异常。我更喜欢这种方式,因为 API 的实现者可以只查看状态代码并继续前进,如果它是预期的客户端错误,则无需反序列化。一位同事认为,如果它是应用程序的预期状态,那么一切都应该是 200,其中包含失败原因的响应正文而不是预期的响应对象,因为执行 catch 块是一项昂贵的操作。我个人不喜欢这种方法,因为您的 API 的实现者必须检查响应实体并尝试将其反序列化为预期的对象,或者如果失败将其反序列化为某个已知的异常对象,因为它始终为 200。

TLDR:我的问题是我看到很多关于为什么执行 catch 块可能会很昂贵的文章,具体取决于堆栈跟踪,并且错误处理不应该是控制流的一部分,但我很难同意这一点,因为Spring 具有 @ControllerAdvice@ExceptionHandler 或更新的 ResponseStatusException 等功能。我也不同意 HTTP 状态码指的是协议,而 200 只是意味着成功传输的说法。如果是这种情况,那么 401 和 403 应该是 200,因为响应处理得很好,客户端只是没有授权或正确的授权。这个论点对我说,一切都应该是 200,为什么还要使用其他状态码?我还处于职业生涯的早期,所以如果我遗漏了一些明显的东西,请原谅。

【问题讨论】:

  • 执行 catch 块很便宜;唯一昂贵的是在新的异常对象上填充堆栈跟踪。你是对的,任何像"error": true 这样的 2xx 响应都违反了 HTTP 的明确合同;查看the long list of error codes 以演示预期通过 4xx 发出信号的类型。
  • 假设在一个例子中,客户端通过 id 向用户发出请求,而服务器没有找到用户是 404 还是 200 响应体为空?对我来说这是 404 对我的同事来说是 200 tools.ietf.org/html/rfc7231#section-6.5.4
  • /users?id=1234 返回[],因为集合资源是多值的。 /users/1234 返回 404。
  • 这是有道理的。关于使用异常进行控制流,我仍然很难做出正确的反应。您提到了the only thing that's costly is filling in stack traces on new exception objects,但这是应用程序流程的一部分,以便调用`@ControllerAdvice 等,因此它会定期发生。
  • 在应用程序的 Java 部分(在 Web 边界内),使用按契约设计的原则:API 应该解释前置条件、预期输入、后置条件和返回值。例外就是正常规则的例外(比如用IOException 说“我不能这样做,因为网络出现故障”)。决定是使用 nullOptional.empty() 还是 NotFoundException 取决于在您的业务建模中什么是有意义的,并且不要让您的模型仅仅符合假设的性能标准。

标签: java spring spring-boot rest error-handling


【解决方案1】:

首先,您不能在每种情况下都跳过 try-catch 块。如果你的代码抛出了一些异常,你应该清楚地知道那些已知的异常,以便完全处理不间断的服务或阻止应用程序发生意外行为。如果您必须捕获或处理异常,则捕获并处理它。

关于处理异常的成本:

永远不要试图在 catch 块中写太多的业务逻辑。只需保持简单并处理或传播它。没有什么可担心的成本。在服务器应用程序中,有时您必须通过捕获异常来跟踪故障并为此编写适当的日志。此外,“尝试”几乎没有任何费用。代码的元数据不是在运行时设置 try 的工作,而是在编译时构建的,这样当抛出异常时,它现在会执行一个相对昂贵的操作,即遍历堆栈并查看是否存在任何可能的 try 块捕捉这个异常。

关于 HttpStatus 代码:

Http 不会强制你应用 200 作为成功的状态码。您还可以使用 401 或 403 作为响应代码。 Http有一些标准。喜欢(400 表示错误请求,403 禁止)如果你不遵循它没有害处。但是,您正在打破标准。例如,您为我提供了一个返回 JSON 作为响应的 API。但是,我希望成功操作 200。如果您将 401 设置为您的成功代码,我将不得不面对一些困境才能找到问题。所以,最好还是遵循标准。

【讨论】:

  • 我很困惑你说“没有什么可担心的成本”,然后在同一段落中引用“相对昂贵的堆栈操作”。此外,401 vs 200 场景很容易识别,但假设在一个示例中,客户端通过 id 向用户发出请求,而服务器没有找到用户是 404 还是 200 响应体为空?对我来说这是 404 对我的同事来说是 200 tools.ietf.org/html/rfc7231#section-6.5.4
  • 如果你想遵循标准,我会说,用 404 码来响应,而不是 200。如果你这样做,那么调用者可以很容易地识别出该 id 可能不在系统中。使用适当的错误消息和状态代码来确认客户端也是一个好主意。并且堆栈内存相对更快。假设它没有被递归调用淹没,您不必担心它。
【解决方案2】:

在正确的场景下,应该使用一系列的 http 状态代码。在我看来 200 (Http.OK) 应该只用于成功响应。如果你正在做一个 POST,我通常会做一个 201,因为这意味着 Http.Created。如果发生错误,您可以使用 Http.UNATHORISED (400) 或 400 和 500 系列中的任何一个来指示错误,然后抛出异常。这比每次抛出 200 后跟异常有用得多。干净代码中的鲍勃叔叔说,您应该始终抛出异常,而不仅仅是状态代码,因此您的想法在一定程度上是正确的。

【讨论】:

  • 谢谢我已经阅读了干净的代码,但已经有一段时间了。我会重温那一章。
【解决方案3】:

客户端通过 id 向用户发出请求,而服务器没有找到用户是 404 还是 200 响应体为空?

在上面你提到的情况下,应该是200。当没有找到URL时,你需要使用404。同样,在您的所有 API 中,您可以以通用方式处理它们,正面 - 200 或 201、负面 500. 404 以及您可以在 @ControllerAdvice 或 @Exceptional Handler 类中处理的其他 4xx 错误。 有时,我们也会在 Authentication/Authorization 中使用 4xx。

【讨论】:

  • 找不到资源时使用404。在这种情况下,用户就是资源。
  • 如果 url 类似于 /user/123 且路径变量是用户 ID,那该怎么办?如果客户端期望用户对象反序列化,响应正文应该是什么?
  • 无论您的客户端是浏览器还是移动端,几乎不可能在所有 API 中处理 404 和其他 4xx 错误。在您提到的上述情况下,找不到具有 123 id 的用户。它应该返回一个 200 的响应代码和一个空对象。如果对象为空,客户端必须处理。
  • 从项目层面来看,所有API都包含在内,如果我们需要关于每个API的一些元数据,那么,在这种情况下,资源将成为数据和元数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-18
  • 2014-01-27
  • 2015-08-10
  • 2017-07-28
相关资源
最近更新 更多