简体   繁体   English

Symfony:不能总是捕获异常

[英]Symfony: cannot always catch exception

Sorry for this vague title, I didn't know how to title my question. 抱歉,标题含糊,我不知道如何为我的问题加上标题。

I'm listening on kernel.exception via the kernel.event_listener service. 我在听上kernel.exception通过kernel.event_listener服务。 I use it in my API to catch all exceptions and serialize them in JSON for a clean error handling for the API customers. 我在API中使用它来捕获所有异常,并以JSON序列化它们,以便为API客户提供干净的错误处理。 I have to adapt the serialization depending on the exception types (my HTTP exceptions, Symfony HTTP exceptions, and others). 我必须根据异常类型(我的HTTP异常,Symfony HTTP异常和其他类型)调整序列化。

When a user is not authenticated when accessing a section restricted by access_control in security.yml, Symfony throws a non-HTTP Symfony\\Component\\Security\\Core\\Exception\\InsufficientAuthenticationException . 当访问security.yml中受access_control限制的部分时未对用户进行身份验证时,Symfony会抛出非HTTP Symfony\\Component\\Security\\Core\\Exception\\InsufficientAuthenticationException In my serializer, a non-HTTP exception is converted in a 500 error. 在我的序列化程序中,非HTTP异常转换为500错误。 Since an InsufficientAuthenticationException is rather a 401 Unauthorized error, I have to catch this exception separately and convert it in my app-specific exception type. 由于InsufficientAuthenticationException相当是401未经授权的错误,因此我必须分别捕获此异常并将其转换为我的应用程序特定的异常类型。

Example: 例:

# 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...

That works. 这样可行。 The Symfony authentication exception is catched, then converted in EUnauthorized, and then EUnauthorized is serialized into JSON. 捕获Symfony身份验证异常,然后将其转换为EUnauthorized,然后将EUnauthorized序列化为JSON。 But you can see that I throw the exception without message or previous exception. 但是您可以看到,我抛出的异常没有消息或previous异常。 Because I want to do this: 因为我要这样做:

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);
}

When I do this (so, just adding two arguments), the serialization stops working, my event listener is not called and the app crashes (in prod, this will turn into a friendly WSoD): 当我这样做时(因此,只需添加两个参数),序列化将停止工作,不会调用我的事件侦听器,并且应用程序崩溃(在生产中,这将变成友好的WSoD):

在此处输入图片说明

Why? 为什么?

In the first "if" you extract data for serialization, in the second you are just rethrowing a new exception. 在第一个“ if”中,提取数据进行序列化,在第二个中,您只是抛出新的异常。

This new exception does not go the kernel.exception flow anymore. 这个新的异常不会再进入kernel.exception流。 It is correctly just thrown: as you can see you have the full stack of exceptions shown. 正确地抛出了它:如您所见,您已显示了完整的异常堆栈。

Ideally, you should end your onKernelException with some kind of Response. 理想情况下,您应该以某种Response结束onKernelException。

EDIT 编辑

I'll expand a bit my previous answer with references to the Symfony documentation and code. 我将通过引用Symfony文档和代码来扩展以前的答案。

The HttpKernel docs say HttpKernel文档

If an exception is thrown at any point inside HttpKernel::handle, another event - kernel.exception is thrown. 如果在HttpKernel :: handle中的任何点抛出异常,则将引发另一个事件-kernel.exception。 Internally, the body of the handle function is wrapped in a try-catch block. 在内部,handle函数的主体包装在try-catch块中。 When any exception is thrown, the kernel.exception event is dispatched so that your system can somehow respond to the exception. 引发任何异常时,将调度kernel.exception事件,以便您的系统可以某种方式响应该异常。

So, your listener is called after an exception in the handle function, but, as you can see in source no try/catch is provided by the handleException function. 因此,您将在handle函数中的异常之后调用您的侦听器,但是,正如您在源代码中看到的那样, handleException函数没有提供try / catch。 This basically means that an Exception thrown in your listener should not be caught. 这基本上意味着不应捕获在侦听器中引发的异常。

In your listener you could swap the current exception with a new one with $event->setException(...) or just try to build a Response yourself. 在您的监听器中,您可以使用$event->setException(...)将当前异常替换为新异常,或者尝试自己构建一个Response

In any case, throwing a new Exception does not seem the proper way here. 无论如何,抛出新的异常似乎不是这里的正确方法。 I sadly don't know why you code works with or without parameters without all the code involved. 可悲的是,我不知道为什么在不涉及所有代码的情况下,您的代码为何可以使用或不使用参数。

I don't know if it helps here, but I had similar problem my event listener is not called and the app crashes . 我不知道这是否对您有帮助,但是我遇到了类似的问题, 未调用事件监听器,应用崩溃了 So i worked around that and overrided one method in Kernel.php file like that: 所以我解决了这个问题,并在Kernel.php文件中重写了一种方法, 如下所示:

protected function initializeContainer() {

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

You can also hook up to other Kernel methods and override them. 您还可以连接到其他内核方法并覆盖它们。
Notice: I'm using Symfony 4.2.* 注意:我正在使用Symfony 4.2。*

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM