ApiProblemException
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.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeThe ApiProblem
object knows everything about how the response should look, including the status code and the response body information. So, it'd be great to have an easy way to convert this into a Response.
But, I want to go further. Sometimes, having a Response isn't enough. Like in processForm()
: since nothing uses its return value. So the only way to break the flow is by throwing an exception.
Here's the goal: create a special exception class, pass it the ApiProblem
object, and then have some central layer convert that into our beautiful API problem JSON formatted response. So whenever something goes wrong, we'll just need to create the ApiProblem
object and then throw this special exception. That'll be it, in any situation.
Create the ApiProblemException
In the Api
directory, create a new class called ApiProblemException
. Make this extend HttpException
- because I like that ability to set the status code on this:
// ... lines 1 - 2 | |
namespace AppBundle\Api; | |
use Symfony\Component\HttpKernel\Exception\HttpException; | |
class ApiProblemException extends HttpException | |
{ | |
} |
Next, we need to be able to attach an ApiProblem
object to this exception class, so that we have access to it later when we handle all of this. Let's pass this via the constructor. Use cmd+n
- or go to the "Generate" menu at the top - and override the __construct
method. Now, add ApiProblem $apiProblem
as the first argument. Also create an $apiProblem
property and set this there:
// ... lines 1 - 6 | |
class ApiProblemException extends HttpException | |
{ | |
private $apiProblem; | |
public function __construct(ApiProblem $apiProblem, $statusCode, $message = null, \Exception $previous = null, array $headers = array(), $code = 0) | |
{ | |
$this->apiProblem = $apiProblem; | |
parent::__construct($statusCode, $message, $previous, $headers, $code); | |
} | |
} |
This won't do anything special yet: this is still just an HttpException
that happens to have an ApiProblem
attached to it.
Back in ProgrammerController
, we can start using this. Throw a new ApiProblemException
. Pass it $apiProblem
as the first argument and 400 next:
// ... lines 1 - 142 | |
private function processForm(Request $request, FormInterface $form) | |
{ | |
$data = json_decode($request->getContent(), true); | |
if ($data === null) { | |
$apiProblem = new ApiProblem(400, ApiProblem::TYPE_INVALID_REQUEST_BODY_FORMAT); | |
throw new ApiProblemException( | |
$apiProblem, | |
400 | |
); | |
} | |
// ... lines 154 - 156 | |
} | |
// ... lines 158 - 193 |
Run the test:
./bin/phpunit -c app --filter testInvalidJson
It still acts like before: with a 400 status code, and now an exception with no message.
Simplifying the ApiProblemException Constructor
Before we handle this, we can make one minor improvement. Remove the $statusCode
and $message
arguments because we can get those from the ApiProblem
itself. Replace that with $statusCode = $apiProblem->getStatusCode()
. And I just realized I messed up my first line - make sure you have $this->apiProblem = $apiProblem
. Also add $message = $apiProblem->getTitle()
:
// ... lines 1 - 6 | |
class ApiProblemException extends HttpException | |
{ | |
// ... lines 9 - 10 | |
public function __construct(ApiProblem $apiProblem, \Exception $previous = null, array $headers = array(), $code = 0) | |
{ | |
$this->apiProblem = $apiProblem; | |
$statusCode = $apiProblem->getStatusCode(); | |
$message = $apiProblem->getTitle(); | |
parent::__construct($statusCode, $message, $previous, $headers, $code); | |
} | |
} |
Hey wait! ApiProblem
doesn't have a getTitle()
method yet. Ok, let's go add one. Use the Generate menu again, select "Getters" and choose title
:
// ... lines 1 - 7 | |
class ApiProblem | |
{ | |
// ... lines 10 - 59 | |
public function getTitle() | |
{ | |
return $this->title; | |
} | |
} |
In ProgrammerController
, simplify this:
// ... lines 1 - 142 | |
private function processForm(Request $request, FormInterface $form) | |
{ | |
$data = json_decode($request->getContent(), true); | |
if ($data === null) { | |
$apiProblem = new ApiProblem(400, ApiProblem::TYPE_INVALID_REQUEST_BODY_FORMAT); | |
throw new ApiProblemException($apiProblem); | |
} | |
// ... lines 151 - 153 | |
} | |
// ... lines 155 - 190 |
It'll figure out the status code and message for us.
./bin/phpunit -c app --filter testInvalidJson
The exception class is perfect - we just need to add that central layer that'll convert this into the beautiful API Problem JSON response. Instead of this HTML stuff.
Hi everyone,
At the end of the video I have this error on my code
(1/1) ServiceCircularReferenceException
Circular reference detected for service "AppBundle\Api\ApiProblemException", path: "AppBundle\Api\ApiProblemException -> AppBundle\Api\ApiProblemException"..
I tried few things, it seems it has something to do with the $statusCode from the ApiProblemException.
Any idea?
Thanks,
Julien