tbbc / rest-util-bundle
Bundle for integrating tbbc/rest-util lib in a Symfony application.
Installs: 194 193
Dependents: 1
Suggesters: 0
Security: 0
Stars: 30
Watchers: 8
Forks: 5
Open Issues: 0
Type:symfony-bundle
Requires
- symfony/framework-bundle: >=2.0
- tbbc/rest-util: ~1.0.0
Requires (Dev)
- phpunit/phpunit: 3.*
- symfony/form: >=2.0
This package is not auto-updated.
Last update: 2024-12-09 22:48:17 UTC
README
A bundle for integrating tbbc/rest-util lib in a Symfony application
Table of contents
Installation
Using Composer, just $ composer require tbbc/rest-util-bundle
package or:
{ "require": { "tbbc/rest-util-bundle": "@stable" } }
Quick start
Handling errors in a REST(ful) API
#### 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"
Custom Symfony Exception Listener
<?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>
Api Controller code
<?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 } }
MyApiCustomException error factory
<?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" }
Usage
Run the test
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
Contributing
- Take a look at the list of issues.
- Fork
- Write a test (for either new feature or bug)
- Make a PR
Requirements
- PHP 5.3+
Authors
- Benjamin Dulau - benjamin.dulau@gmail.com
- Valentin Ferriere - valentin@v-labs.fr
License
The Big Brains Company - TbbcRestUtilBundle
is licensed under the MIT License - see the LICENSE file for details