Skip to content

Add ErrorHandler component #31065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 27, 2019
Merged

Conversation

yceruto
Copy link
Member

@yceruto yceruto commented Apr 10, 2019

Q A
Branch? 4.4
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? yes
Tests pass? no
Fixed tickets #25905, #26448
License MIT
Doc PR TODO

Mainly for API-based apps that don't require TwigBundle to get the correct exception response according to the request format (aka _format attribute).

exception_response

✔️ RFC7807 compliant for JSON and XML formats.


This introduce a new ErrorRenderer service that render a FlattenException into a given format:

use Symfony\Component\ErrorHandler\ErrorRenderer\ErrorRenderer;
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
use Symfony\Component\ErrorHandler\ErrorRenderer\JsonErrorRenderer;

$renderers = [
    new HtmlErrorRenderer(),
    new JsonErrorRenderer(),
    // ...
];
$errorRenderer = new ErrorRenderer($renderers);

return new Response(
    $errorRenderer->render($exception, $request->getRequestFormat()), 
    $exception->getStatusCode(), 
    $exception->getHeaders()
);

The built-in error renderers are:

Format Class
html HtmlErrorRenderer
json JsonErrorRenderer
xml, atom XmlErrorRenderer
txt TxtErrorRenderer

And you can add your own error renderer by implementing the ErrorRendererInterface and tagging it with error_handler.renderer in your service definition.

Creating your own error renderer for a built-in format will end up replacing the related built-in error renderer.

Demo: https://github.com/yceruto/error-handler-app (add custom error renderer)

@yceruto yceruto changed the title [ErrorException] Add ErrorException component Add ErrorException component Apr 10, 2019
@yceruto yceruto force-pushed the error_exception_component branch from 42d5a64 to 3f9b448 Compare April 10, 2019 16:28
@Simperfit
Copy link
Contributor

This is gonna be really nice !

@nicolas-grekas nicolas-grekas added this to the next milestone Apr 11, 2019
@yceruto yceruto force-pushed the error_exception_component branch from 3f9b448 to 342ee6e Compare April 11, 2019 19:31
@nicolas-grekas
Copy link
Member

Would it make sense no name the component ErrorHandler?

@yceruto
Copy link
Member Author

yceruto commented Apr 12, 2019

Would it make sense no name the component ErrorHandler?

Does that make me think that we should move ErrorHandler and ExceptionHandler classes from the Debug component to here, also? Currently, this component only solve the convertion of the exception to a formatted output (html, json, etc) but there is no register()/handle() method involved at this moment.

@stof
Copy link
Member

stof commented Apr 12, 2019

The discussion we had with @fabpot and @nicolas-grekas in Brussels concluded that we should indeed create a new component for error handling. Having the production error handling being a responsibility of a component named debug looks weird, as prod is not in debug mode.

@stof
Copy link
Member

stof commented Apr 12, 2019

I don't think having an ErrorException class being responsible both for representing the Exception and for rendering it in multiple formats is the right architecture for the component though. This is not extendable at all, and cannot even be configurable (as you cannot instantiate the rendering layer in a place configuring it).

To me, we need 3 separate things in the component:

  • the ExceptionHandler/ErrorHandler (moved from Debug)
  • the value object representing the actual exception
  • a renderer layer knowing how to render the exception in different format (HTML, JSON corresponding to some standard format, XML, etc...)

And then, the Twig-based HTML error page should ideally become a hook into this renderer layer rather than being a totally separate rendering system.

@yceruto
Copy link
Member Author

yceruto commented Apr 12, 2019

I agree, we should also discuss about BC then, there is a lot of exception classes in Debug component used by these two handlers and we wouldn't want this new component require to symfony/debug :)

@yceruto yceruto force-pushed the error_exception_component branch 3 times, most recently from cb2ac97 to 50ec30a Compare April 12, 2019 19:34
@yceruto yceruto changed the title Add ErrorException component Add ErrorHandler component Apr 12, 2019
@yceruto yceruto force-pushed the error_exception_component branch from 50ec30a to cc01b78 Compare April 12, 2019 19:56
@yceruto
Copy link
Member Author

yceruto commented Apr 12, 2019

Update

  • Component renamed to "ErrorHandler" as suggested by Nicolas (it's a better name IMO too).
  • Almost everything was moved from the Debug component because interdependence between handlers -> classes/interface, except Debug class.

Still working on the renderer layer...

Copy link
Contributor

@Taluu Taluu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about being able to support more formats ? Wouldn't it be better to have some sort of factory so that we can use whatever format we want ?

@yceruto yceruto force-pushed the error_exception_component branch from cc01b78 to 8d641e3 Compare April 12, 2019 20:10
@yceruto
Copy link
Member Author

yceruto commented Apr 12, 2019

How about being able to support more formats ? Wouldn't it be better to have some sort of factory so that we can use whatever format we want ?

Thanks @Taluu for your input, yes I'm working on it.

@yceruto yceruto force-pushed the error_exception_component branch from 8d641e3 to a82888e Compare April 13, 2019 14:43
@yceruto
Copy link
Member Author

yceruto commented Apr 14, 2019

Update

  • Added error renderer layer with interface + DI tag error_handler.renderer to add custom format/renderer:
interface ErrorRendererInterface
{
    /**
     * Gets the format of the content.
     *
     * @return string The content format
     */
    public static function getFormat(): string;

    /**
     * Renders an Exception and returns the Response content.
     *
     * @return string The Response content as a string
     */
    public function render(FlattenException $exception): string;
}
  • Added FrameworkBundle integration.

@yceruto
Copy link
Member Author

yceruto commented Apr 14, 2019

And then, the Twig-based HTML error page should ideally become a hook into this renderer layer rather than being a totally separate rendering system.

There are many changes (a big diff), most are movements + deprecations, so I prefer to leave this last issue for another PR

@yceruto yceruto force-pushed the error_exception_component branch from ed3480a to 3c05010 Compare April 14, 2019 01:25
@yceruto
Copy link
Member Author

yceruto commented Apr 14, 2019

@yceruto yceruto marked this pull request as ready for review April 14, 2019 02:47
@fabpot
Copy link
Member

fabpot commented Jun 27, 2019

Thank you @yceruto.

@fabpot fabpot merged commit 7057244 into symfony:4.4 Jun 27, 2019
fabpot added a commit that referenced this pull request Jun 27, 2019
This PR was merged into the 4.4 branch.

Discussion
----------

Add ErrorHandler component

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | no
| Fixed tickets | #25905, #26448
| License       | MIT
| Doc PR        | TODO

Mainly for API-based apps that don't require TwigBundle to get the correct exception response according to the request format (aka `_format` attribute).

![exception_response](https://user-images.githubusercontent.com/2028198/55509651-713dc700-562a-11e9-8b98-bef3b0229397.gif)

:heavy_check_mark: [RFC7807](https://tools.ietf.org/html/rfc7807) compliant for JSON and XML formats.

---

This introduce a new `ErrorRenderer` service that render a `FlattenException` into a given format:
```php
use Symfony\Component\ErrorHandler\ErrorRenderer\ErrorRenderer;
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
use Symfony\Component\ErrorHandler\ErrorRenderer\JsonErrorRenderer;

$renderers = [
    new HtmlErrorRenderer(),
    new JsonErrorRenderer(),
    // ...
];
$errorRenderer = new ErrorRenderer($renderers);

return new Response(
    $errorRenderer->render($exception, $request->getRequestFormat()),
    $exception->getStatusCode(),
    $exception->getHeaders()
);
```

The built-in error renderers are:

| Format | Class |
| --- | --- |
| html | HtmlErrorRenderer |
| json | JsonErrorRenderer |
| xml, atom | XmlErrorRenderer |
| txt | TxtErrorRenderer |

And you can add your own error renderer by implementing the `ErrorRendererInterface` and tagging it with `error_handler.renderer` in your service definition.

Creating your own error renderer for a built-in format will end up replacing the related built-in error renderer.

Demo: https://github.com/yceruto/error-handler-app ([add custom error renderer](yceruto/error-handler-app@06fc647))

Commits
-------

7057244 Added ErrorHandler component
@yceruto yceruto deleted the error_exception_component branch June 27, 2019 12:46
@driesvints
Copy link
Contributor

driesvints commented Jul 4, 2019

@fabpot @yceruto the removed public methods like getContent and getStylesheet on the Symfony\Component\Debug\ExceptionHandler class are breaking changes according to SemVer. Can you please revert these removals or introduce this new functionality on 5.0 instead?

These changes will, for example break Lumen: laravel/lumen-framework#935

@Tobion
Copy link
Contributor

Tobion commented Jul 4, 2019

please open a new issue

@driesvints
Copy link
Contributor

@Tobion done: #32371

nicolas-grekas added a commit that referenced this pull request Jul 9, 2019
…(1st step) (yceruto)

This PR was squashed before being merged into the 4.4 branch (closes #32377).

Discussion
----------

[Debug] Restoring back the state of the Debug component (1st step)

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #32371
| License       | MIT

After a good discussion with @nicolas-grekas, we made the decision to split the current `ErrorCatcher` component into several:
 * `ErrorHandler` it would be the Debug component before these changes #31065, with everything related to ErrorHandler, Debug, DebugClassLoader classes and change its name.
 * `ErrorDumper` it would be the current ErrorCatcher but with FlattenException + the new error renderer system only.

This is the first step, then we can deprecate everything for the Debug component in favor of the ErrorHandler and ErrorDumper components, **BUT without moving any code !!**, that would give us more freedom to do it correctly in the new components.

NOTE: For this PR I've copy the `Debug` component directory from the revision prior to merged commit  #31065 in 4.4 branch.

Commits
-------

eda49e2 [Debug] Restoring back the state of the Debug component (1st step)
symfony-splitter pushed a commit to symfony/var-dumper that referenced this pull request Jul 9, 2019
…(1st step) (yceruto)

This PR was squashed before being merged into the 4.4 branch (closes #32377).

Discussion
----------

[Debug] Restoring back the state of the Debug component (1st step)

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | symfony/symfony#32371
| License       | MIT

After a good discussion with @nicolas-grekas, we made the decision to split the current `ErrorCatcher` component into several:
 * `ErrorHandler` it would be the Debug component before these changes symfony/symfony#31065, with everything related to ErrorHandler, Debug, DebugClassLoader classes and change its name.
 * `ErrorDumper` it would be the current ErrorCatcher but with FlattenException + the new error renderer system only.

This is the first step, then we can deprecate everything for the Debug component in favor of the ErrorHandler and ErrorDumper components, **BUT without moving any code !!**, that would give us more freedom to do it correctly in the new components.

NOTE: For this PR I've copy the `Debug` component directory from the revision prior to merged commit  symfony/symfony#31065 in 4.4 branch.

Commits
-------

eda49e295e [Debug] Restoring back the state of the Debug component (1st step)
Tobion added a commit that referenced this pull request Jul 24, 2019
…formats and using ErrorRenderer as fallback (yceruto)

This PR was merged into the 4.4 branch.

Discussion
----------

[TwigBundle] Deprecating error templates for non-html formats and using ErrorRenderer as fallback

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

In the previous [PR](#31065) we created a new mechanism to render any PHP error/exception in a formatted string, which if the FB is enabled, would return an HTTP Response according to the preferred Request format (html, json, xml, txt, etc.), but when installing the TwigBundle this rendering mechanism is replaced by the current ExceptionController.

This ExceptionController allows us to render custom error pages based on Twig in many formats, just what is already supported with the new ErrorRenderer component, so let's deprecate this in favor of the native.

Commits
-------

bf0c24a Deprecating error templates for non-html formats and using ErrorRenderer
@nicolas-grekas nicolas-grekas modified the milestones: next, 4.4 Oct 27, 2019
This was referenced Nov 12, 2019
@ghost
Copy link

ghost commented Dec 7, 2019

ErrorHandler respecting Content-Type is great in API projects! Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.