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

Tightening up the Response

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

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

This endpoint is missing two teeny-tiny details.

Setting Content-Type: application/json

First, we're returning JSON, but the Response Content-Type is still advertising that we're returning text/html. That's a bummer, and will probably confuse some clients, like jQuery's AJAX function.

It's easy to fix anyways: set new Response to a $response variable like we did earlier and call $response->headers->set() with Content-Type and application/json:

... lines 1 - 41
public function showAction($nickname)
{
... lines 44 - 54
$response = new Response(json_encode($data), 200);
$response->headers->set('Content-Type', 'application/json');
return $response;
}
... lines 60 - 61

Check out the new Content-Type header:

php testing.php

404'ing

The second teeny-tiny thing we're missing is a 404 on a bad $nickname. Just treat this like a normal controller - so if (!$programmer), then throw $this->createNotFoundException(). And we might as well give ourselves a nice message:

... lines 1 - 41
public function showAction($nickname)
{
$programmer = $this->getDoctrine()
->getRepository('AppBundle:Programmer')
->findOneByNickname($nickname);
if (!$programmer) {
throw $this->createNotFoundException(sprintf(
'No programmer found with nickname "%s"',
$nickname
));
}
... lines 54 - 65
}
... lines 67 - 68

Use a fake nickname in testing.php temporarily to try this:

29 lines testing.php
... lines 1 - 23
// 2) GET a programmer resource
$response = $client->get('/api/programmers/abcd'.$nickname);
echo $response;
echo "\n\n";

Then re-run:

php testing.php

Woh! That exploded! This is Symfony's HTML exception page. It is our 404 error, but it's in HTML instead of JSON. Why? Internally, Symfony has a request format, which defaults to html. If you change that to json, you'll get JSON errors. If you're curious about this, google for Symfony request _format.

But I'll show you this later in the series. And we'll go one step further to completely control the format of our errors. And it will be awesome.

Change the URL in testing.php back to the real nickname.

Setting the Location Header

Ok, remember that fake Location header on the POST endpoint? Good news! We can get rid of that fake URL.

First, give the GET endpoint route a name - api_programmers_show:

... lines 1 - 42
/**
* @Route("/api/programmers/{nickname}", name="api_programmers_show")
* @Method("GET")
*/
public function showAction($nickname)
... lines 48 - 73

Copy that, call $this->generateUrl(), pass it api_programmers_show and the array with the nickname key set to the nickname of this new Programmer. Then just set this on the Location header... instead of our invented URL:

... lines 1 - 18
public function newAction(Request $request)
{
... lines 21 - 32
$response = new Response('It worked. Believe me - I\'m an API', 201);
$programmerUrl = $this->generateUrl(
'api_programmers_show',
['nickname' => $programmer->getNickname()]
);
$response->headers->set('Location', $programmerUrl);
return $response;
}
... lines 42 - 72

Why are we doing this again? Just because it might be helpful to your client to have the address to the new resource. That would be especially true if you used an auto-increment id that the server just determined.

To try this in testing.php, copy the echo $response stuff, put it below the first $response, then let's die:

33 lines testing.php
... lines 1 - 18
// 1) Create a programmer resource
$response = $client->post('/api/programmers', [
'body' => json_encode($data)
]);
echo $response;
echo "\n\n";
die;
... lines 27 - 33

Now, try php testing.php:

php testing.php

Now we have a really clean Location header we could use to fetch or edit that Programmer.

Use the Location Header

Heck, we can even use this and get rid of the hardcoded URL in testing.php. Set $programmerUrl to $response->getHeader('Location'). Pop that in to the next get() call:

31 lines testing.php
... lines 1 - 23
$programmerUrl = $response->getHeader('Location');
// 2) GET a programmer resource
$response = $client->get($programmerUrl);
echo $response;
echo "\n\n";

I like that! When you're testing your API, you're really eating your own dog food. And that's a perfect time to think about the user-experience of getting work done with it.

Try it one last time:

php testing.php

That looks great!

Leave a comment!

0
Login or Register to join the conversation
Cat in space

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

This tutorial uses an older version of Symfony. The concepts of REST 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