Buy

Centralize that Response!

Check out the response - it's got a Content-Type of text/html. I thought we fixed that! Well, that's no surprise - when we switched from JsonResponse to Response, we lost that header. But more importantly, this mistake is too easy to make: we're calling serialize() and then creating the Response by hand in every controller. That means we'd need to set this header everywhere. That sucks. Let's centralize this across our entire project.

First, move serialize() out of ProgrammerController and into a class called BaseController. This is something I created and all controllers extend this. Paste this at the bottom and make it protected:

129 lines src/AppBundle/Controller/BaseController.php
... lines 1 - 15
abstract class BaseController extends Controller
{
... lines 18 - 122
protected function serialize($data, $format = 'json')
{
return $this->container->get('jms_serializer')
->serialize($data, $format);
}
}

And while we're here - make another function: protected function createApiResponse(). Give it two arguments: $data and $statusCode that defaults to 200:

129 lines src/AppBundle/Controller/BaseController.php
... lines 1 - 15
abstract class BaseController extends Controller
{
... lines 18 - 113
protected function createApiResponse($data, $statusCode = 200)
{
... lines 116 - 120
}
... lines 122 - 127
}

Instead of creating the Response ourselves, we can just call this and it'll take care of the details. Inside, first serialize the $data - whatever that is. And then return a new Response() with that $json, that $statusCode and - most importantly - that Content-Type header of application/json so we don't forget to set that:

129 lines src/AppBundle/Controller/BaseController.php
... lines 1 - 113
protected function createApiResponse($data, $statusCode = 200)
{
$json = $this->serialize($data);
return new Response($json, $statusCode, array(
'Content-Type' => 'application/json'
));
}
... lines 122 - 129

I love it! Let's use this everywhere! Search for new Response. Call $response = $this->createApiResponse() and pass the $programmer. Copy that line and make sure it's status code is 201. Remove the other stuff, but keep the line that sets the Location header:

140 lines src/AppBundle/Controller/Api/ProgrammerController.php
... lines 1 - 15
class ProgrammerController extends BaseController
{
... lines 18 - 21
public function newAction(Request $request)
{
... lines 24 - 33
$response = $this->createApiResponse($programmer, 201);
... lines 35 - 38
$response->headers->set('Location', $programmerUrl);
return $response;
}
... lines 43 - 138
}

Ok, much easier. Find the rest of the new Response spots and update them. It's all pretty much the same - listAction() has a different variable name, but that's it. For deleteAction(), well, it's returning a null Response, so we can leave that one alone.

140 lines src/AppBundle/Controller/Api/ProgrammerController.php
... lines 1 - 47
public function showAction($nickname)
{
... lines 50 - 60
$response = $this->createApiResponse($programmer, 200);
return $response;
}
... lines 65 - 69
public function listAction()
{
... lines 72 - 75
$response = $this->createApiResponse(['programmers' => $programmers], 200);
return $response;
}
... lines 80 - 84
public function updateAction($nickname, Request $request)
{
... lines 87 - 104
$response = $this->createApiResponse($programmer, 200);
return $response;
}
... lines 109 - 140

Let's re-run the tests!

phpunit -c app

They still fail, but the responses have the right Content-Type header.

Time to fix these failures, and see how we can control the serializer.

Leave a comment!