Skip to content

Commit 054921e

Browse files
Add Exception configuration documentation
1 parent 52f75b5 commit 054921e

File tree

2 files changed

+224
-19
lines changed

2 files changed

+224
-19
lines changed

core/errors.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# Error Handling
2+
3+
API Platform Core allows to customize the HTTP status code sent to the clients when exceptions are thrown.
4+
5+
```yaml
6+
# app/config/config.yml
7+
8+
api_platform:
9+
# Map exceptions to HTTP status codes using the `exception_to_status` configuration key
10+
exception_to_status:
11+
# The 2 following exceptions are handled by default
12+
Symfony\Component\Serializer\Exception\ExceptionInterface: 400 # Use a raw status code (recommended)
13+
ApiPlatform\Core\Exception\InvalidArgumentException: 'HTTP_BAD_REQUEST' # Or with a constant of `Symfony\Component\HttpFoundation\Response`
14+
15+
AppBundle\Exception\ProductNotFoundException: 404 # Custom exceptions can easily be handled
16+
```
17+
18+
As in any php application, your exceptions have to extends the \Exception class or any of it's children.
19+
20+
```php
21+
<?php
22+
23+
// src/AppBundle/Exception/ProductNotFoundException.php
24+
25+
namespace AppBundle\Exception;
26+
27+
final class ProductNotFoundException extends \Exception
28+
{
29+
}
30+
```
31+
32+
```php
33+
<?php
34+
35+
// src/AppBundle/EventSubscriber/CartManager.php
36+
37+
namespace AppBundle\EventSubscriber;
38+
39+
use ApiPlatform\Core\EventListener\EventPriorities;
40+
use AppBundle\Entity\Product;
41+
use AppBundle\Exception\ProductNotFoundException;
42+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
43+
use Symfony\Component\HttpFoundation\Request;
44+
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
45+
use Symfony\Component\HttpKernel\KernelEvents;
46+
47+
final class ProductManager implements EventSubscriberInterface
48+
{
49+
private const DOESNOTEXISTS = 51;
50+
private const DEACTIVATED = 52;
51+
private const OUTOFSTOCK = 53;
52+
53+
public static function getSubscribedEvents()
54+
{
55+
return [
56+
KernelEvents::REQUEST => [['checkProductAvailability', EventPriorities::POST_DESERIALIZE]],
57+
];
58+
}
59+
60+
public function checkProductAvailability(GetResponseForControllerResultEvent $event)
61+
{
62+
$product = $event->getControllerResult();
63+
$method = $event->getRequest()->getMethod();
64+
65+
if (!$product instanceof Product || Request::METHOD_GET !== $method) {
66+
return;
67+
}
68+
69+
if (!$product->getVirtualStock()) {
70+
// Using internal codes for a better understanding of what's going on
71+
throw new ProductNotFoundException(self::OUTOFSTOCK);
72+
}
73+
}
74+
}
75+
```
76+
77+
The exception doesn't have to be a Symfony's `HttpException`. Any type of `Exception` can be thrown. The best part is that API Platform already takes care of how the error is handled and returned. For instance, if the API is configured to respond in JSON-LD, the error will be returned in this format as well.
78+
79+
```json
80+
{
81+
"@context": "/contexts/Error",
82+
"@type": "Error",
83+
"hydra:title": "An error occurred",
84+
"hydra:description": "53"
85+
}
86+
```
87+
88+
Is what you get, with an HTTP status code 404 as defined in the configuration.
89+
90+
## Validation errors
91+
92+
API Platform does handle the validation errors responses for you. You can define a Symfony supported constraint, or a custom constraint upon any `ApiResource` or it's properties.
93+
94+
```php
95+
<?php
96+
97+
// src/AppBundle/Entity/Product.php
98+
99+
namespace AppBundle\Entity;
100+
101+
use ApiPlatform\Core\Annotation\ApiResource;
102+
use Doctrine\ORM\Mapping as ORM;
103+
use Symfony\Component\Validator\Constraints as Assert;
104+
use AppBundle\Validator\Constraints as AppAssert;
105+
106+
/**
107+
*
108+
* @ApiResource
109+
* @ORM\Entity
110+
*/
111+
class Product
112+
{
113+
/**
114+
* @var int The id of this product.
115+
*
116+
* @ORM\Id
117+
* @ORM\GeneratedValue
118+
* @ORM\Column(type="integer")
119+
*/
120+
private $id;
121+
122+
/**
123+
* @var string The name of the product
124+
*
125+
* @ORM\Column
126+
* @Assert\NotBlank
127+
*/
128+
private name;
129+
130+
/**
131+
* @var ProductProperty[] Describe the product
132+
*
133+
* @ORM\Column(type="array")
134+
* @AppAssert\MinimalProperties
135+
*/
136+
private $properties;
137+
}
138+
```
139+
140+
```php
141+
<?php
142+
143+
// src/AppBundle/Validator/Constraints/MinimalProperties.php
144+
145+
namespace AppBundle\Validator\Constraints;
146+
147+
use Symfony\Component\Validator\Constraint;
148+
149+
/**
150+
* @Annotation
151+
*/
152+
class MinimalProperties extends Constraint
153+
{
154+
public $message = 'The product must have the minimal properties required (description, price)';
155+
156+
public function validatedBy()
157+
{
158+
return get_class($this).'Validator';
159+
}
160+
}
161+
```
162+
163+
```php
164+
<?php
165+
166+
// src/AppBundle/Validator/Constraints/MinimalPropertiesValidator.php
167+
168+
namespace AppBundle\Validator\Constraints;
169+
170+
use Symfony\Component\Validator\Constraint;
171+
use Symfony\Component\Validator\ConstraintValidator;
172+
173+
/**
174+
* @Annotation
175+
*/
176+
final class MinimalPropertiesValidator extends ConstraintValidator
177+
{
178+
public function validate($value, Constraint $constraint)
179+
{
180+
if (!in_array('description', $value) || !in_array('price', $value)) {
181+
$this->context->buildViolation($constraint->message)
182+
->addViolation();
183+
}
184+
}
185+
}
186+
```
187+
188+
API Platform will handle the error returned and adapt it's format according to the API configuration. If you did configured it to respond in JSON-LD. Your response would looks like:
189+
190+
```json
191+
{
192+
"@context": "/contexts/ConstraintViolationList",
193+
"@type": "ConstraintViolationList",
194+
"hydra:title": "An error occurred",
195+
"hydra:description": "properties: The product must have the minimal properties required (description, price)",
196+
"violations": [
197+
{
198+
"propertyPath": "properties",
199+
"message": "The product must have the minimal properties required (description, price)"
200+
}
201+
]
202+
}
203+
```

index.md

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,27 +58,29 @@
5858
1. [Using Validation Groups](core/validation.md#using-validation-groups)
5959
2. [Dynamic Validation Groups](core/validation.md#dynamic-validation-groups)
6060
3. [Error Levels and Payload Serialization](core/validation.md#error-levels-and-payload-serialization)
61-
9. [Pagination](core/pagination.md)
61+
9. [Error Handling](core/errors.md)
62+
1. [Converting PHP Exceptions to HTTP Errors](core/errors.md#converting-php-exceptions-to-http-errors)
63+
10. [Pagination](core/pagination.md)
6264
1. [Disabling the Pagination](core/pagination.md#disabling-the-pagination)
6365
2. [Changing the Number of Items per Page](core/pagination.md#changing-the-number-of-items-per-page)
6466
3. [Partial Pagination](core/pagination.md#partial-pagination)
65-
10. [The Event System](core/events.md)
66-
11. [Content Negotiation](core/content-negotiation.md)
67+
11. [The Event System](core/events.md)
68+
12. [Content Negotiation](core/content-negotiation.md)
6769
1. [Enabling Several Formats](core/content-negotiation.md#enabling-several-formats)
6870
2. [Registering a Custom Serializer](core/content-negotiation.md#registering-a-custom-serializer)
6971
3. [Creating a Responder](core/content-negotiation.md#creating-a-responder)
7072
4. [Writing a Custom Normalizer](core/content-negotiation.md#writing-a-custom-normalizer)
71-
12. [Using External JSON-LD Vocabularies](core/external-vocabularies.md)
72-
13. [Extending JSON-LD context](core/extending-jsonld-context.md)
73-
14. [Data Providers](core/data-providers.md)
73+
13. [Using External JSON-LD Vocabularies](core/external-vocabularies.md)
74+
14. [Extending JSON-LD context](core/extending-jsonld-context.md)
75+
15. [Data Providers](core/data-providers.md)
7476
1. [Custom Collection Data Provider](core/data-providers.md#custom-collection-data-provider)
7577
2. [Custom Item Data Provider](core/data-providers.md#custom-item-data-provider)
7678
3. [Injecting the Serializer in an `ItemDataProvider`](core/data-providers.md#injecting-the-serializer-in-an-itemdataprovider)
77-
15. [Extensions](core/extensions.md)
79+
16. [Extensions](core/extensions.md)
7880
1. [Custom Extension](core/extensions.md#custom-extension)
7981
2. [Filter upon the current user](core/extensions.md#example)
80-
16. [Security](core/security.md)
81-
17. [Performance](core/performance.md)
82+
17. [Security](core/security.md)
83+
18. [Performance](core/performance.md)
8284
1. [Enabling the Builtin HTTP Cache Invalidation System](core/performance.md#enabling-the-builtin-http-cache-invalidation-system)
8385
2. [Enabling the Metadata Cache](core/performance.md#enabling-the-metadata-cache)
8486
3. [Using PPM (PHP-PM)](core/performance.md#using-ppm-php-pm)
@@ -90,35 +92,35 @@
9092
3. [Override at Resource and Operation Level](core/performance.md#override-at-resource-and-operation-level)
9193
4. [Disable Eager Loading](core/performance.md#disable-eager-loading)
9294
3. [Partial Pagination](core/performance.md#partial-pagination)
93-
18. [Operation Path Naming](core/operation-path-naming.md)
95+
19. [Operation Path Naming](core/operation-path-naming.md)
9496
1. [Configuration](core/operation-path-naming.md#configuration)
9597
2. [Create a Custom Operation Path Naming](core/operation-path-naming.md#create-a-custom-operation-path-resolver)
9698
1. [Defining the Operation Path Naming](core/operation-path-naming.md#defining-the-operation-path-resolver)
9799
2. [Registering the Service](core/operation-path-naming.md#registering-the-service)
98100
3. [Configure it](core/operation-path-naming.md#configure-it)
99-
19. [Accept application/x-www-form-urlencoded Form Data](core/form-data.md)
100-
20. [FOSUserBundle Integration](core/fosuser-bundle.md)
101+
20. [Accept application/x-www-form-urlencoded Form Data](core/form-data.md)
102+
21. [FOSUserBundle Integration](core/fosuser-bundle.md)
101103
1. [Installing the Bundle](core/fosuser-bundle.md#installing-the-bundle)
102104
2. [Enabling the Bridge](core/fosuser-bundle.md#enabling-the-bridge)
103105
3. [Creating a `User` Entity with Serialization Groups](core/fosuser-bundle.md#creating-a-user-entity-with-serialization-groups)
104-
21. [Adding a JWT authentication using LexikJWTAuthenticationBundle](core/jwt.md)
106+
22. [Adding a JWT authentication using LexikJWTAuthenticationBundle](core/jwt.md)
105107
1. [Testing with Swagger](core/jwt.md#testing-with-swagger)
106108
2. [Testing with Behat](core/jwt.md#testing-with-behat)
107-
22. [NelmioApiDocBundle integration](core/nelmio-api-doc.md)
108-
23. [AngularJS Integration](core/angularjs-integration.md)
109+
23. [NelmioApiDocBundle integration](core/nelmio-api-doc.md)
110+
24. [AngularJS Integration](core/angularjs-integration.md)
109111
1. [Restangular](core/angularjs-integration.md#restangular)
110112
2. [ng-admin](core/angularjs-integration.md#ng-admin)
111-
24. [Swagger Support](core/swagger.md)
113+
25. [Swagger Support](core/swagger.md)
112114
1. [Override Swagger documentation](core/swagger.md#override-swagger-documentation)
113-
25. [GraphQL Support](core/graphql.md)
115+
26. [GraphQL Support](core/graphql.md)
114116
1. [Overall View](core/graphql.md#overall-view)
115117
2. [Enabling GraphQL](core/graphql.md#enabling-graphql)
116118
3. [GraphiQL](core/graphql.md#graphiql)
117-
26. [The Serialization Process](core/serialization.md)
119+
27. [The Serialization Process](core/serialization.md)
118120
1. [Overall Process](core/serialization.md#overall-process)
119121
2. [Available Serializers](core/serialization.md#available-serializers)
120122
3. [Decorating a Serializer and Add Extra Data](core/serialization.md#decorating-a-serializer-and-add-extra-data)
121-
27. [Handling Data Transfer Objects (DTOs)](core/dto.md)
123+
28. [Handling Data Transfer Objects (DTOs)](core/dto.md)
122124

123125
## The Schema Generator Component: Generate Data Models from Open Vocabularies
124126

0 commit comments

Comments
 (0)