Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Request Format: Why Exceptions Return HTML

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

When you throw an exception in Symfony - even an HttpException - it returns an HTML page. Notice the Content-Type header here of text/html. And in reality, this is returning a full, giant HTML exception page - my test helpers are just summarizing things.

Why is that? Why does Symfony default to the idea that if something goes wrong, it should return HTML?

Request Format

Here's the answer: for every single request, Symfony has what's called a "request format", and it defaults to html. But there are a number of different ways to say "Hey Symfony, the user wants json, so if something goes wrong, give them that".

The easiest way to set the request format is in your routing. Open up app/config/routing.yml:

... lines 1 - 4
app_api:
resource: "@AppBundle/Controller/Api"
type: annotation

When we import the routes from our API controllers, we want all of them to have a json request format. To do that, add a defaults key. Below that, set a magic key called _format to json:

... lines 1 - 4
app_api:
resource: "@AppBundle/Controller/Api"
type: annotation
defaults:
_format: json

For us, this is optional, because in a minute, we're going to completely take control of exceptions for our API. But with just this, re-run the tests:

./bin/phpunit -c app --filter testInvalidJson

Yes! Now we get a Content-Type header of application/json and because we're in the dev environment, it returns the full stack trace as JSON.

This is cool. But the JSON structure still won't be right. So let's take full control using our ApiProblemException.

Leave a comment!

5
Login or Register to join the conversation
Bartlomeij Avatar
Bartlomeij Avatar Bartlomeij | posted 3 years ago

Hey Guys!
Any ideas how to set default json format at Symfony 5? :)
Thanks!

Reply

Hey Bartlomeij !

There are 2 ways to do this:

1) Assuming you're using annotation routes, in your config/packages/routes/annotations.yaml file, you can do what we did in this video:


controllers:
    resource: ../../src/Controller/
    type: annotation
    defaults:
        _format: json

2) The above approach will make all annotation routes use JSON by default. But, if a user goes to a 404 page that doesn't match any route, they would still have an HTML. To fix that, you would register a listener on the kernel.request event (also called the RequestEvent), get the request, and call $request->setRequestFormat('json').

Let me know if that helps!

Cheers!

Reply
Default user avatar

Just thinking ahead, but I hope I can just override a method in ApiProblemException that returns a customized Response :)

Reply

Hey Johan!

Unfortunately, there's nothing built in for that, but I like your thinking :). Of course, we have the HttpException class (really, the HttpExceptionInterface) and its sub-classes, but the default ExceptionListener in Symfony (which handles the response during an exception... by default), only uses this interface to get the status code and any headers you might want set. It still *always* renders this internal controller that always returns HTML. Well, as we learned here, it can also return JSON, but it's this more-or-less hardcoded, generic JSON page - you don't have a lot of control.

So, good thinking... but it doesn't work that way :).

Cheers!

Reply
Default user avatar

Maybe in Symfony 4, haha ;)

I love your elaborate answers, thanks!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses an older version of Symfony. The concepts of REST and errors are still valid, but I recommend using API Platform in new Symfony apps.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "2.6.*", // v2.6.11
        "doctrine/orm": "~2.2,>=2.2.3,<2.5", // v2.4.7
        "doctrine/dbal": "<2.5", // v2.4.4
        "doctrine/doctrine-bundle": "~1.2", // v1.4.0
        "twig/extensions": "~1.0", // v1.2.0
        "symfony/assetic-bundle": "~2.3", // v2.6.1
        "symfony/swiftmailer-bundle": "~2.3", // v2.3.8
        "symfony/monolog-bundle": "~2.4", // v2.7.1
        "sensio/distribution-bundle": "~3.0,>=3.0.12", // v3.0.21
        "sensio/framework-extra-bundle": "~3.0,>=3.0.2", // v3.0.7
        "incenteev/composer-parameter-handler": "~2.0", // v2.1.0
        "hautelook/alice-bundle": "0.2.*", // 0.2
        "jms/serializer-bundle": "0.13.*" // 0.13.0
    },
    "require-dev": {
        "sensio/generator-bundle": "~2.3", // v2.5.3
        "behat/behat": "~3.0", // v3.0.15
        "behat/mink-extension": "~2.0.1", // v2.0.1
        "behat/mink-goutte-driver": "~1.1.0", // v1.1.0
        "behat/mink-selenium2-driver": "~1.2.0", // v1.2.0
        "phpunit/phpunit": "~4.6.0" // 4.6.4
    }
}
userVoice