A bundle for integrating tbbc/rest-util lib in a Symfony application
Using Composer, just $ composer require tbbc/rest-util-bundle
package or:
{
"require": {
"tbbc/rest-util-bundle": "@stable"
}
}
####Â Configuration
tbbc_rest_util:
error:
use_bundled_factories: true
exception_mapping:
FormErrorException:
class: "Tbbc\\RestUtilBundle\\Error\\Exception\\FormErrorException"
factory: tbbc_rest_util_form_error
http_status_code: 400
error_code: 400101
error_message: "Invalid input"
error_more_info_url: "http://api.my.tld/doc/error/400101"
AccessDeniedException:
class: "Symfony\\Component\\Security\\Core\\AccessDeniedException"
factory: default
http_status_code: 401
error_code: 401001
error_message: "Access denied"
extended_message: "The given token don't have enough privileges for accessing to this resource"
error_more_info_url: "http://api.my.tld/doc/error/401001"
CustomException:
class: "My\\ApiBundle\\Exception\\CustomException"
factory: my_api_custom
http_status_code: 501
error_code: 501001
error_more_info_url: "http://api.my.tld/doc/error/501001"
Exception:
class: "\\Exception"
factory: default
http_status_code: 500
error_code: 501203
error_message: "Server error"
<?php
namespace My\ApiBundle\EventListener;
// ... other use statements
use Tbbc\RestUtil\Error\ErrorResolverInterface;
class RestExceptionListener extends ExceptionListener
{
private $errorResolver;
public function __construct(ErrorResolverInterface $errorResolver, $controller, LoggerInterface $logger = null)
{
$this->errorResolver = $errorResolver;
parent::__construct($controller, $logger);
}
/**
* @param GetResponseForExceptionEvent $event
* @return void
*/
public function onKernelException(GetResponseForExceptionEvent $event)
{
static $handling;
if (true === $handling) {
return;
}
$exception = $event->getException();
$error = $this->errorResolver->resolve($exception);
if (null == $error) {
return;
}
$handling = true;
$response = new Response(json_encode($error->toArray()), $error->getHttpStatusCode(), array(
'Content-Type' => 'application/json'
));
$event->setResponse($response);
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::EXCEPTION => array('onKernelException', 10),
);
}
}
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="my_api.event_listener.rest_exception.class">My\ApiBundle\EventListener\RestExceptionListener</parameter>
</parameters>
<services>
<service id="my_api.event_listener.rest_exception" class="%my_api.event_listener.rest_exception.class%">
<tag name="kernel.event_subscriber" />
<tag name="monolog.logger" channel="request" />
<argument type="service" id="tbbc_rest_util.error.error_resolver" />
<argument>%twig.exception_listener.controller%</argument>
<argument type="service" id="logger" on-invalid="null" />
</service>
</services>
</container>
<?php
namespace My\ApiBundle\Controller;
// ... other use statements
use Symfony\Component\Security\Core\AccessDeniedException;
use Tbbc\RestUtilBundle\Error\Exception\FormErrorException;
use My\ApiBundle\Exception\CustomException;
class PostCommentsController extends Controller
{
public function postCommentsAction($postId)
{
// ... fetch $post with $postId
if (!$this->get('security.context')->isGranted('COMMENT', $post)) {
throw new AccessDeniedException('Access denied');
}
$commentResource = new CommentResource();
$form = $this->createNamedForm('', new CommentResourceType(), $commentResource);
$form->bind($this->getRequest());
if (!$form->isValid()) {
throw new FormErrorException($form);
}
// another error
if (....) {
throw new CustomException('Something bad just happened!');
}
// ... save comment or whatever
}
}
<?php
namespace My\ApiBundle\Error\Factory;
use Tbbc\RestUtil\Error\Error;
use Tbbc\RestUtil\Error\ErrorFactoryInterface;
use Tbbc\RestUtil\Error\Mapping\ExceptionMappingInterface;
use My\ApiBundle\Exception\CustomException;
class CustomErrorFactory implements ErrorFactoryInterface
{
/**
* {@inheritDoc}
*/
public function getIdentifier()
{
return 'my_api_custom';
}
/**
* {@inheritDoc}
*/
public function createError(\Exception $exception, ExceptionMappingInterface $mapping)
{
if (!$this->supportsException($exception)) {
return null;
}
$errorMessage = $mapping->getErrorMessage();
if (empty($errorMessage)) {
$errorMessage = $exception->getMessage();
}
$extendedMessage = $exception->getMoreExtendedMessage();
// Or whatever you need to do with your exception here
return new Error($mapping->getHttpStatusCode(), $mapping->getErrorCode(), $errorMessage,
$extendedMessage, $mapping->getErrorMoreInfoUrl());
}
/**
* {@inheritDoc}
*/
public function supportsException(\Exception $exception)
{
return $exception instanceof CustomException;
}
}
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="my_api.error.custom_error_factory.class">My\ApiBundle\Error\Factory\CustomErrorFactory</parameter>
</parameters>
<services>
<service id="my_api.error.custom_error_factory" class="%my_api.error.custom_error_factory.class%">
<tag name="tbbc_rest_util.error_factory" />
</service>
</services>
</container>
ENJOY!
For the exceptions thrown in the previous PostCommentsController
class example, the response body will be respectively
something like the following:
For the AccessDeniedException exception:
{
"http_status_code": 401,
"code": 401001,
"message": "Access denied",
"extended_message": "The given token don't have enough privileges for accessing to this resource",
"more_info_url": "http:\/\/api.my.tld\/doc\/error\/401001"
}
For the FormErrorException exception:
{
"http_status_code": 400,
"code": 400101,
"message": "Invalid input",
"extended_message": {
"global_errors": [
"Bubbled form error!"
],
"property_errors": {
"content": [
"The comment content should not be blank",
]
}
},
"more_info_url": "http:\/\/api.my.tld\/doc\/error\/400101"
}
For the CustomException exception:
{
"http_status_code": 501,
"code": 501001,
"message": "Something bad just happened!",
"extended_message": null,
"more_info_url": "http:\/\/api.my.tld\/doc\/error\/501001"
}
First make sure you have installed all the dependencies, run:
$ composer install --dev
then, run the test from within the root directory:
$ vendor/bin/phpunit
- Take a look at the list of issues.
- Fork
- Write a test (for either new feature or bug)
- Make a PR
- PHP 5.3+
- Benjamin Dulau - benjamin.dulau@gmail.com
- Valentin Ferriere - valentin@v-labs.fr
The Big Brains Company - TbbcRestUtilBundle
is licensed under the MIT License - see the LICENSE file for details