Building API endpoints in Symfony often involves the same repetitive boilerplate:
inject the serializer service, call serialize(), create a response with the
proper status code, and configure the Content-Type header:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// src/Controller/GetUserController.php
namespace App\Controller;
use App\Model\User;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Serializer\SerializerInterface;
final readonly class GetUserController
{
public function __construct(
private SerializerInterface $serializer
) {
}
public function __invoke(): JsonResponse
{
$data = new User(1, 'Jane Smith', '...');
$serialized = $this->serializer->serialize($data, 'json');
return JsonResponse::fromJsonString($serialized, JsonResponse::HTTP_CREATED);
}
}
Symfony 8.1 streamlines this workflow with the new #[Serialize] attribute.
After installing and configuring the Serializer component, apply the
#[Serialize] attribute to a controller method and return an object or
array instead of a full Response object.
Symfony serializes the result using the format derived from the request (JSON by
default), sets the Content-Type header, and wraps everything in a Response
object automatically:
1 2 3 4 5 6 7 8 9 10 11
// ...
use Symfony\Component\HttpKernel\Attribute\Serialize;
final readonly class GetUserController
{
#[Serialize]
public function __invoke(): User
{
return new User(1, 'Jane Smith', '...');
}
}
You can also customize the HTTP status code, response headers, and serialization context passed to the Serializer component:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// ...
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
final readonly class CreateProductController
{
#[Serialize(
code: 201,
headers: ['X-Custom-Header' => 'abc'],
context: [DateTimeNormalizer::FORMAT_KEY => 'd.m.Y H:i:s'],
)]
public function __invoke(): ProductCreated
{
// ... create the product
return new ProductCreated(101);
}
}
The response format is determined from the current request format, so the same
controller can return JSON or XML depending on the route configuration. For
example, a route defined as /products/{id}.{_format} produces JSON for
/products/42.json and XML for /products/42.xml.
If the request asks for a format that is not supported by the Serializer
component, Symfony automatically returns a 415 Unsupported Media Type response.
nice for making it more easier. but it was already pretty easy with the
->json()return path right? :).Cool. Thank you for your work!
It seems to be it would've been smarter to have the attribute on the response object's class rather than on the controller - that way you can control the status code based on the response object.