【问题标题】:Symfony: cannot always catch exceptionSymfony:不能总是捕获异常
【发布时间】:2015-08-19 07:56:50
【问题描述】:

抱歉这个模糊的标题,我不知道如何命名我的问题。

我正在通过kernel.event_listener 服务收听kernel.exception。我在我的 API 中使用它来捕获所有异常并在 JSON 中将它们序列化,以便为 API 客户进行干净的错误处理。 我必须根据异常类型(我的 HTTP 异常、Symfony HTTP 异常等)调整序列化。

如果用户在访问 security.yml 中受access_control 限制的部分时未通过身份验证,Symfony 会抛出非 HTTP Symfony\Component\Security\Core\Exception\InsufficientAuthenticationException。在我的序列化程序中,非 HTTP 异常被转换为 500 错误。由于InsufficientAuthenticationException 是一个401 Unauthorized 错误,我必须单独捕获此异常并将其转换为我的应用程序特定的异常类型。

例子:

# Find appropriate serialization
if($ex instanceof HttpErr\HttpErrorInterface) {
    # AppBundle\Exceptions\Http\*
    # A displayable error thrown intentionally by our controllers
    $status  = $ex->getStatusCode();
    $message = $ex->getMessage();
    $other   = $ex->getAdditionalDatas();
} elseif($ex instanceof InsufficientAuthenticationException) {
    throw new HttpErr\EUnauthorized; # look a this line
}
# more elseifs...

这行得通。捕获 Symfony 认证异常,然后转换为 EUnauthorized,然后将 EUnauthorized 序列化为 JSON。但是你可以看到我抛出了没有消息的异常或previous 异常。 因为我想这样做:

elseif($ex instanceof InsufficientAuthenticationException) {
    # the standard argument $previous is in 2nd position in my exceptions instead of being 3rd.
    # the previous argument is important for me since it will keep context in error propagation.
    throw new HttpErr\EUnauthorized($ex->getMessage(), $ex);
}

当我这样做时(因此,只需添加两个参数),序列化停止工作,我的事件侦听器没有被调用并且应用程序崩溃(在 prod 中,这将变成一个友好的 WSoD):

为什么?

【问题讨论】:

    标签: php symfony exception-handling


    【解决方案1】:

    在第一个“如果”中,您提取数据进行序列化,在第二个中,您只是重新抛出一个新异常。

    这个新异常不再进入 kernel.exception 流程。它只是正确地抛出:如您所见,您显示了完整的异常堆栈。

    理想情况下,您应该以某种响应结束 onKernelException。

    编辑

    我将通过参考 Symfony 文档和代码来扩展我之前的答案。

    HttpKernel docs

    如果在 HttpKernel::handle 内的任何点抛出异常,则会抛出另一个事件 - kernel.exception。在内部,句柄函数的主体被包装在一个 try-catch 块中。当任何异常被抛出时,kernel.exception 事件就会被调度,以便您的系统能够以某种方式响应异常。

    因此,您的侦听器在 handle 函数中的异常之后被调用,但是,正如您在 source 中看到的那样,handleException 函数没有提供 try/catch。这基本上意味着不应捕获在您的侦听器中引发的异常。

    在您的侦听器中,您可以用 $event->setException(...) 的新异常交换当前异常,或者尝试自己构建一个 Response

    无论如何,在这里抛出一个新的异常似乎不是正确的方法。很遗憾,我不知道为什么您的代码在不涉及所有代码的情况下使用或不使用参数。

    【讨论】:

    • 我觉得你读的太快了。我说throw new HttpErr\EUnauthorized;(注意没有参数)有效。它序列化。但是当我添加两个参数时,它不起作用(没有捕获异常)。所以有一个意想不到的行为。而在异常监听器中抛出异常并不意味着新的异常不必再次被捕获!
    • 编辑并扩展了我上面的答案。
    【解决方案2】:

    我不知道这是否有帮助,但我遇到了类似的问题我的事件监听器没有被调用并且应用程序崩溃。所以我解决了这个问题并覆盖了 Kernel.php 文件中的一种方法,如下所示:

    protected function initializeContainer() {
    
        try {
            $container = parent::initializeContainer();
        } catch (\Throwable $throwable){
            // MY CATCH CODE GOES HERE
        }
        return $container;
    }
    

    您还可以连接到其他内核方法并覆盖它们。
    注意:我使用的是 Symfony 4.2.*

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多