If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeThis endpoint is missing two teeny-tiny details.
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
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:
... 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.
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:
... 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.
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:
... 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!
"Houston: no signs of life"
Start the conversation!
// 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
}
}