Skip to content

Commit 1d69305

Browse files
authored
Merge pull request #878 from dunglas/patch
Add docs for PATCH, and various improvements
2 parents 62ee8d9 + 8a86d67 commit 1d69305

6 files changed

+904
-787
lines changed

core/content-negotiation.md

Lines changed: 107 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,46 @@
11
# Content Negotiation
22

33
The API system has built-in [content negotiation](https://en.wikipedia.org/wiki/Content_negotiation) capabilities.
4-
It leverages the [`willdurand/negotiation`](https://github.com/willdurand/Negotiation) library.
54

65
By default, only the [JSON-LD](https://json-ld.org) format is enabled. However API Platform Core supports many more formats and can be extended.
76

8-
The framework natively supports JSON-LD, GraphQL, JSONAPI, HAL, raw JSON, XML, YAML and CSV (YAML and CSV support is only available if you use Symfony 3.2+).
7+
The framework natively supports JSON-LD (and Hydra), GraphQL, JSON:API, HAL, YAML, CSV, HTML (API docs), raw JSON and raw XML.
8+
Using the raw JSON or raw XML formats is discouraged, prefer using JSON-LD instead, which provides more feature and is as easy to use.
99

10-
Both XML and JSON formats are experimental and there is no assurance that we will not break them.
10+
API Platform also supports [JSON Merge Patch (RFC 7396)](https://tools.ietf.org/html/rfc7396) the JSON:API [`PATCH`](https://tools.ietf.org/html/rfc5789) formats, as well as [Problem Details (RFC 7807)](https://tools.ietf.org/html/rfc7807), Hydra and JSON:API error formats.
1111

1212
API Platform Core will automatically detect the best resolving format depending on:
1313

1414
* enabled formats (see below)
15-
* the requested format, specified in either the `Accept` HTTP header or as an extension appended to the URL
15+
* the requested format, specified in either [the `Accept` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) or as an extension appended to the URL
1616

1717
Available formats are:
1818

1919
Format | Format name | MIME types | Backward Compatibility guaranteed
2020
----------------------------------------------------------------|--------------|-------------------------------|----------------------------------------
2121
[JSON-LD](https://json-ld.org) | `jsonld` | `application/ld+json` | yes
2222
[GraphQL](graphql.md) | n/a | n/a | yes
23-
[JSONAPI](http://jsonapi.org/) | `jsonapi` | `application/vnd.api+json` | yes
23+
[JSON:API](http://jsonapi.org/) | `jsonapi` | `application/vnd.api+json` | yes
2424
[HAL](http://stateless.co/hal_specification.html) | `jsonhal` | `application/hal+json` | yes
25-
[JSON](https://www.json.org/) | `json` | `application/json` | no
26-
[XML](https://www.w3.org/XML/) | `xml` | `application/xml`, `text/xml` | no
2725
[YAML](http://yaml.org/) | `yaml` | `application/x-yaml` | no
2826
[CSV](https://tools.ietf.org/html/rfc4180) | `csv` | `text/csv` | no
2927
[HTML](https://whatwg.org/) (API docs) | `html` | `text/html` | no
28+
[XML](https://www.w3.org/XML/) | `xml` | `application/xml`, `text/xml` | no
29+
[JSON](https://www.json.org/) | `json` | `application/json` | no
3030

31-
If the client's requested format is not specified (if it's not supported, it will throw an HTTP bad request error), the response format will be the first format defined in the `formats` configuration key (see below).
32-
An example using the built-in XML support is available in [Behat specs](https://github.com/api-platform/core/blob/master/features/main/content_negotiation.feature).
31+
If the client's requested format is not specified, the response format will be the first format defined in the `formats` configuration key (see below).
32+
If the request format is not supported, an [Unsupported Media Type](https://developer.mozilla.org/fr/docs/Web/HTTP/Status/415) error will be returned.
3333

34-
The API Platform content negotiation system is extendable. Support for other formats can be added by [creating and registering appropriate encoders and, sometimes, normalizers](https://symfony.com/doc/current/serializer.html#adding-normalizers-and-encoders). Adding support for other
35-
standard hypermedia formats upstream is welcome. Don't hesitate to contribute by adding your encoders and normalizers
36-
to API Platform Core.
34+
Examples showcasing how to use the different mechanisms are available [in the API Platform test suite](https://github.com/api-platform/core/blob/master/features/main/content_negotiation.feature).
3735

38-
## Enabling Several Formats
36+
## Configuring Formats Globally
3937

4038
The first required step is to configure allowed formats. The following configuration will enable the support of XML (built-in)
4139
and of a custom format called `myformat` and having `application/vnd.myformat` as [MIME type](https://en.wikipedia.org/wiki/Media_type).
4240

4341
```yaml
4442
# api/config/packages/api_platform.yaml
4543
api_platform:
46-
# ...
47-
4844
formats:
4945
jsonld: ['application/ld+json']
5046
jsonhal: ['application/hal+json']
@@ -54,7 +50,6 @@ api_platform:
5450
yaml: ['application/x-yaml']
5551
csv: ['text/csv']
5652
html: ['text/html']
57-
myformat: ['application/vnd.myformat']
5853
```
5954
6055
To enable GraphQL support, [read the dedicated chapter](graphql.md).
@@ -63,10 +58,43 @@ Because the Symfony Serializer component is able to serialize objects in XML, se
6358
`text/xml` string as value is enough to retrieve XML documents from our API. However API Platform knows nothing about the
6459
`myformat` format. We need to register an encoder and optionally a normalizer for this format.
6560

61+
## Configuring PATCH Formats
62+
63+
By default, API Platform supports JSON Merge Patch and JSON:API PATCH formats.
64+
Support for the JSON:API PATCH format is automatically enabled if JSON:API support is enabled.
65+
JSON Merge Patch support must be enabled explicitly:
66+
67+
```yaml
68+
# api/config/packages/api_platform.yaml
69+
api_platform:
70+
patch_formats:
71+
json: ['application/merge-patch+json']
72+
jsonapi: ['application/vnd.api+json']
73+
```
74+
75+
When support for at least one PATCH format is enabled, [an `Accept-Patch` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Patch) containing the list of supported patch formats is automatically added to all HTTP responses for items.
76+
77+
## Configuring Error Formats
6678

67-
## Enabling Additional Formats On a Specific Resource/Operation
79+
API Platform will try to send to the client the error format matching with the format request with the `Accept` HTTP headers (or the URL extension). For instance, if a client request a JSON-LD representation of a resource, and an error occurs, then API Platform will serialize this error using the Hydra format (Hydra is a vocabulary for JSON-LD containing a standard representation of API errors).
6880

69-
Support for specific formats can also be added at resource or operation level, using the `formats` attribute.
81+
Available formats can also be configured:
82+
83+
```yaml
84+
# api/config/packages/api_platform.yaml
85+
api_platform:
86+
error_formats:
87+
jsonproblem: ['application/problem+json']
88+
jsonld: ['application/ld+json'] # Hydra error formats
89+
jsonapi: ['application/vnd.api+json']
90+
```
91+
92+
## Configuring Formats For a Specific Resource or Operation
93+
94+
Support for specific formats can also be configured at resource and operation level using the `input_formats` and `output_formats` attributes.
95+
`input_formats` controls the formats accepted in request bodies while `output_formats` controls formats available for responses.
96+
97+
The `format` attribute can be used as a shortcut, it sets both the `input_formats` and `output_formats` in one time.
7098

7199
```php
72100
<?php
@@ -75,7 +103,7 @@ Support for specific formats can also be added at resource or operation level, u
75103
namespace App\Entity;
76104
77105
/**
78-
* @ApiResource(attributes={"formats"={"xml", "jsonld", "csv"={"text/csv"}}})
106+
* @ApiResource(formats={"xml", "jsonld", "csv"={"text/csv"}})
79107
*/
80108
class Book
81109
{
@@ -89,7 +117,8 @@ Additionally the `csv` format is added with the MIME type `text/csv`.
89117
It is also important to notice that the usage of this attribute will override the formats defined in the configuration, therefore
90118
this configuration might disable the `json` or the `html` on this resource for example.
91119

92-
You can specify different accepted formats at operation level too:
120+
You can specify different accepted formats at operation level too, it's especially convenient for to configure formats available for the `PATH` method:
121+
93122
```php
94123
<?php
95124
// api/src/Entity/Book.php
@@ -98,68 +127,84 @@ namespace App\Entity;
98127
99128
/**
100129
* @ApiResource(
101-
* collectionOperations={"get"={"formats"={"xml"={"text/xml"}}}},
102-
* attributes={"formats"={"jsonld", "csv"={"text/csv"}}}
103-
* )
130+
* formats={"jsonld", "csv"={"text/csv"}},
131+
* itemOperations={
132+
* "patch"={
133+
* "input_formats"={"json"={"application/merge-patch+json"}}
134+
* }
135+
* }
136+
* )
104137
*/
105138
class Book
106139
{
107140
// ...
108141
}
109142
```
110143

111-
As an alternative to annotations, you can also use XML or YAML, the example above would become:
144+
As an alternative to annotations, you can also use XML or YAML, the example above would become, in YAML:
145+
146+
```yaml
147+
resources:
148+
App\Entity\Book:
149+
attributes:
150+
formats:
151+
0: 'jsonld' # format already defined in the config
152+
csv: 'text/csv'
153+
itemOperations:
154+
get:
155+
formats:
156+
json: ['application/merge-patch+json'] # works also with "application/merge-patch+json"
157+
```
158+
159+
160+
Or in XML:
112161

113-
XML:
114162
```xml
115163
<resources xmlns="https://api-platform.com/schema/metadata"
116164
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
117165
xsi:schemaLocation="https://api-platform.com/schema/metadata
118166
https://api-platform.com/schema/metadata/metadata-2.0.xsd">
119167
<resource class="App\Entity\Greeting">
120-
<collectionOperations>
121-
<collectionOperation name="get">
122-
<attribute name="formats">
123-
<attribute name="xml">
124-
<attribute>text/xml</attribute>
125-
</attribute>
126-
<!-- works also with <attribute name="xml">text/xml</attribute> -->
127-
</attribute>
128-
</collectionOperation>
129-
</collectionOperations>
130-
131168
<attribute name="formats">
132169
<attribute>jsonld</attribute> <!-- format already defined in the config -->
133170
<attribute name="csv">text/csv</attribute>
134171
</attribute>
172+
173+
<itemOperations>
174+
<itemOperation name="get">
175+
<attribute name="input_formats">
176+
<attribute name="json">
177+
<attribute>application/merge-patch+json</attribute>
178+
</attribute>
179+
<!-- works also with <attribute name="json">application/merge-patch+json</attribute> -->
180+
</attribute>
181+
</itemOperation>
182+
</itemOperations>
135183
</resource>
136184
</resources>
137185
```
138-
YAML:
139-
```yaml
140-
resources:
141-
App\Entity\Book:
142-
collectionOperations:
143-
get:
144-
formats:
145-
xml: ['text/xml'] # works also with "text/html"
146-
attributes:
147-
formats:
148-
0: 'jsonld' # format already defined in the config
149-
csv: 'text/csv'
150-
```
151186

152-
## Registering a Custom Serializer
187+
## Supporting Custom Formats
188+
189+
The API Platform content negotiation system is extendable.
190+
You can add support for formats not available by default by creating custom normalizer and encoders.
191+
Refer to the Symfony documentation to learn [how to create and register such classes](https://symfony.com/doc/current/serializer.html#adding-normalizers-and-encoders).
192+
193+
Then, register the new format in the configuration:
153194

154-
If you are adding support for a format supported by default neither by API Platform nor by the Symfony Serializer Component,
155-
you need to create a custom encoder, decoder and eventually a normalizer and a denormalizer. Refer to the
156-
Symfony documentation to learn [how to create and register such classes](https://symfony.com/doc/current/cookbook/serializer.html#adding-normalizers-and-encoders).
195+
```yaml
196+
# api/config/packages/api_platform.yaml
197+
api_platform:
198+
formats:
199+
# ...
200+
myformat: ['application/vnd.myformat']
201+
```
157202

158-
API Platform Core will automatically call the serializer with your defined format name (`myformat` in previous examples)
159-
as `format` parameter during the deserialization process. It will then return the result to the client with the requested MIME
160-
type using its built-in responder.
203+
API Platform Core will automatically call the serializer with your defined format name as `format` parameter during the deserialization process (`myformat` in the example).
204+
It will then return the result to the client with the requested MIME type using its built-in responder.
205+
For non-standard formats, [a vendor, vanity or unregistered MIME type should be used](https://en.wikipedia.org/wiki/Media_type#Vendor_tree).
161206

162-
## Writing a Custom Normalizer
207+
### Reusing the API Platform Infrastructure
163208

164209
Using composition is the recommended way to implement a custom normalizer. You can use the following template to start your
165210
own implementation of `CustomItemNormalizer`:
@@ -255,3 +300,8 @@ class CustomItemNormalizer implements NormalizerInterface, DenormalizerInterface
255300
// ...
256301
}
257302
```
303+
304+
### Contributing Support for New Formats
305+
306+
Adding support for **standard** formats upstream is welcome!
307+
We'll be glad to merge new encoders and normalizers in API Platform Core.

0 commit comments

Comments
 (0)