Fosrestbundle: Error handling with 2.8 beta

Created on 13 May 2020  路  20Comments  路  Source: FriendsOfSymfony/FOSRestBundle

I'm trying to upgrade my project to FOSRestBundle 2.8 but can't figure out how to handle errors correctly.

Previously we were relying on the ExceptionListener which is now deprecated. The call stack went like this:

 /src/ApiBundle/Component/Serializer/JsonExceptionNormalizer.php:66
 /src/ApiBundle/Component/Serializer/JsonExceptionNormalizer.php:45
 /vendor/jms/serializer/src/GraphNavigator/SerializationGraphNavigator.php:192
 /vendor/jms/serializer/src/Serializer.php:249
 /vendor/jms/serializer/src/Serializer.php:162
 /vendor/friendsofsymfony/rest-bundle/Serializer/JMSSerializerAdapter.php:62
 /app/cache/test/ContainerS2qAfcv/JMSSerializerAdapter_a5dd94b.php:27
 /vendor/friendsofsymfony/rest-bundle/View/ViewHandler.php:458
 /vendor/friendsofsymfony/rest-bundle/View/ViewHandler.php:424
 /vendor/friendsofsymfony/rest-bundle/View/ViewHandler.php:310
 /vendor/friendsofsymfony/rest-bundle/Controller/ExceptionController.php:67
 /vendor/symfony/http-kernel/HttpKernel.php:158
 /vendor/symfony/http-kernel/HttpKernel.php:80
 /vendor/symfony/http-kernel/EventListener/ErrorListener.php:60
 /vendor/friendsofsymfony/rest-bundle/EventListener/ExceptionListener.php:81

In the JsonExceptionNormalizer I have some custom logic to add validation errors into the final json when the thrown exception contains symfony/validator violations.

What extension point can I use now when the ExceptionListener is deprecated? Or what can I use to trigger my current ViewHandler -> JMSSerializerAdapter -> JsonExceptionNormalizer logic?

Most helpful comment

Looks like I managed to fix it...

fos_rest:
    exception:
        enabled: true
        exception_listener: false
        serializer_error_renderer: true
        serialize_exceptions: false

and updated my JsonExceptionNormalizer:

Screenshot from 2020-05-15 12-05-20

All 20 comments

FosRestBundle did deprecate the ExceptionListener because Symfony has a new component called ErrorHandler which is used instead of that.

If you disable the exception listener you should make sure you disable also the deprecated ExceptionController of Symfony, FosRest should throw you an error if you forgot that here.

If you did correctly disable the deprecated symfony exception controller the following way:

twig:
     exception_controller: null

The symfony error handler component should handle this correctly.

I'm not very familiar with implementation maybe @xabbuh can provide there more, whats best to extend it. In @sulu I did keep it simple and decorated the FosRest Error Normalizer to change the output:

https://github.com/sulu/sulu/pull/4798/files#diff-a097bf1329bf027e774b48ddb459fb5e
https://github.com/sulu/sulu/pull/4798/files#diff-4f4ac11710f21c2c11567806aff5a1a5

Maybe this helps you.

Your FlattenExceptionNormalizer implements Symfony\Component\Serializer\Normalizer\NormalizerInterface. In my case however I'm not using the symfony/serializer component - I'm using JMS Serializer instead. So sadly this doesn't help me. :-(

For the JMS Serializer we have the FlattenExceptionHandler. You then need an ErrorRenderInterface implementation that is able to leverage the JMS serializer which you can register with the serializer_error_renderer option:

fos_rest:
    exception:
        serializer_error_renderer: true

@xabbuh I don't understand... am I supposed to use a custom implementation of serializer_error_renderer? If so why is the option boolean? How do I make rest bundle aware of my implementation?

I did find FlattenExceptionHandler myself too but it only has 2 formats but I need to use my own formatting logic...

@enumag As I see the service just call the serializer: https://github.com/FriendsOfSymfony/FOSRestBundle/blob/ec65a37d4ae87040786f6c046dedc5a5c494278f/ErrorRenderer/SerializerErrorRenderer.php#L69

so you can just create a JMS Serialization Subscriber or define serialization configuration for your exception.

@enumag When you enable that option we will register an error renderer for you which is able to use the Serializer abstraction that FOSRestBundle provides. Thus it will work with Symfony serializer as well as JMS serializer. And if the format provided by the FlattenExceptionHandler doesn't fit your needs, you can indeed register your own handler as the error renderer doesn't do anything special but just uses the serializer.

The SerializerErrorRenderer is never called even though I have it enabled. Do I need to enable something else too? Where is it supposed to be called? Currently the exception is just handled by FlattenExceptionHandler...

Do you have some custom error/exception controller configured (using framework.error_controller or twig.exception_controller)?

Yes, I do...

But it extends the default \Symfony\Component\HttpKernel\EventListener\ErrorListener. I only changed the duplicateRequest method a little bit.

I don't see where the ErrorListener is related here. You will need to make sure that Symfony uses the new ErrorController which will call the error renderer (which should be our one) from where the serializer is called then.

parameters:
    framework.error_controller: AppBundle\EventListener\ExceptionListener

Then you will need to make sure that your own exception listener uses the error renderer.

Actually I'm trying to use custom implementation of SubscribingHandlerInterface instead. But somehow the exception is passed to FlattenExceptionNormalizer instead of FlattenExceptionHandler even though I'm not using symfony/serializer. Also it goes through SerializerErrorRenderer even though I set:

fos_rest:
    exception:
        serializer_error_renderer: false

Oh... it's not the SerializerErrorRenderer from this bundle but \Symfony\Component\ErrorHandler\ErrorRenderer\SerializerErrorRenderer...

Anyway, how do I make the exception to be handled by FlattenExceptionHandler instead of FlattenExceptionNormalizer?

Can you please show the output of bin/console debug:config framework?

Looks like I managed to fix it...

fos_rest:
    exception:
        enabled: true
        exception_listener: false
        serializer_error_renderer: true
        serialize_exceptions: false

and updated my JsonExceptionNormalizer:

Screenshot from 2020-05-15 12-05-20

@enumag That's good news. Do you think we could have worded some upgrade notes more explicitly to make this easier to work?

@xabbuh I kinda feel like my way might not be optimal... In my opinion your documentation should contain a recommended way for custom exception formatting when you want the API to show validation errors. Then the upgrade notes should point to this section as a recommended way to replace the deprecated error_listener / error_controller options.

@xabbuh is it really good way to handle those exception messages as @enumag has shown in https://github.com/FriendsOfSymfony/FOSRestBundle/issues/2200#issuecomment-629150462 ?
As for me, it seems kinda strange that I need to use JsonExceptionNormalizer instead of ExceptionController. I think it is just two different abstractions. What should I do if I want to use ExceptionController ?

Was this page helpful?
0 / 5 - 0 ratings